<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 6.3.0">

  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha256-Z1K5uhUaJXA7Ll0XrZ/0JhX4lAtZFpT6jkKrEDT0drU=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"example.com","root":"/","images":"/images","scheme":"Muse","darkmode":false,"version":"8.14.1","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":false,"style":null},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"menu_item":"fadeInDown","post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"},"path":"/search.xml","localsearch":{"enable":true,"trigger":"auto","top_n_per_article":-1,"unescape":false,"preload":false}}</script><script src="/js/config.js"></script>

    <meta property="og:type" content="website">
<meta property="og:title" content="JsyBlog">
<meta property="og:url" content="http://example.com/page/11/index.html">
<meta property="og:site_name" content="JsyBlog">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="SongyangJi">
<meta name="twitter:card" content="summary">


<link rel="canonical" href="http://example.com/page/11/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":true,"isPost":false,"lang":"zh-CN","comments":"","permalink":"","path":"page/11/index.html","title":""}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>JsyBlog</title>
  








  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <div class="column">
      <header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">JsyBlog</h1>
      <i class="logo-line"></i>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger" aria-label="搜索" role="button">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a></li><li class="menu-item menu-item-tags"><a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a></li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup"><div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off" maxlength="80"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close" role="button">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div class="search-result-container no-result">
  <div class="search-result-icon">
    <i class="fa fa-spinner fa-pulse fa-5x"></i>
  </div>
</div>

    </div>
  </div>

</header>
        
  
  <aside class="sidebar">

    <div class="sidebar-inner sidebar-overview-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">SongyangJi</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">251</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">45</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
          <a href="/tags/">
        <span class="site-state-item-count">109</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>

        </div>
      </div>
    </div>

    
  </aside>


    </div>

    <div class="main-inner index posts-expand">

    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2022/05/07/Sdu-Devops-02/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2022/05/07/Sdu-Devops-02/" class="post-title-link" itemprop="url">使用Jenkins的pipeline实现CI/CD</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2022-05-07 21:00:00" itemprop="dateCreated datePublished" datetime="2022-05-07T21:00:00+08:00">2022-05-07</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-06-08 19:01:34" itemprop="dateModified" datetime="2022-06-08T19:01:34+08:00">2022-06-08</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/DevOps/" itemprop="url" rel="index"><span itemprop="name">DevOps</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="Pipeline"><a href="#Pipeline" class="headerlink" title="Pipeline"></a>Pipeline</h1><h2 id="Prerequisites"><a href="#Prerequisites" class="headerlink" title="Prerequisites"></a>Prerequisites</h2><h3 id="安装Pipeline插件"><a href="#安装Pipeline插件" class="headerlink" title="安装Pipeline插件"></a>安装Pipeline插件</h3><p>在你想创建一条流水线的时候，有可能发现并没有这个UI入口，原因是还没有下载相关插件，所以可以下载插件****，这是一整套和流水线相关的插件。（还有 BlueOcean等其他流水线相关的插件，这里先不使用，只使用classic的pipeline）。</p>
<p>下载完记得重启<code>docker-compose restart</code>。</p>
<h2 id="创建流水线"><a href="#创建流水线" class="headerlink" title="创建流水线"></a>创建流水线</h2><h3 id="Jenkinsfile模板"><a href="#Jenkinsfile模板" class="headerlink" title="Jenkinsfile模板"></a>Jenkinsfile模板</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">pipeline &#123;</span><br><span class="line">    agent any</span><br><span class="line"></span><br><span class="line">    stages &#123;</span><br><span class="line">        stage(&#x27;Stage 1: Fetch code from git&#x27;) &#123;</span><br><span class="line">            steps &#123;</span><br><span class="line">                echo &#x27;Stage 1: Fetch code from git -- SUCCESS&#x27;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        stage(&#x27;Stage 2: Build the project using maven&#x27;) &#123;</span><br><span class="line">            steps &#123;</span><br><span class="line">                echo &#x27;Stage 2: Build the project using maven -- SUCCESS&#x27;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        stage(&#x27;Stage 3: Make a custom image using docker&#x27;) &#123;</span><br><span class="line">            steps &#123;</span><br><span class="line">                echo &#x27;Stage 3: Make a custom image using docker -- SUCCESS&#x27;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        stage(&#x27;Stage 4: Push image to Harbor&#x27;) &#123;</span><br><span class="line">            steps &#123;</span><br><span class="line">                echo &#x27;Stage 4: Push image to Harbor -- SUCCESS&#x27;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        stage(&#x27;Stage 5: Publish over SSH&#x27;) &#123;</span><br><span class="line">            steps &#123;</span><br><span class="line">                echo &#x27;Stage 5: Publish over SSH -- SUCCESS&#x27;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="从GitLab拉取代码"><a href="#从GitLab拉取代码" class="headerlink" title="从GitLab拉取代码"></a>从GitLab拉取代码</h2><p>流水线片段脚本：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">checkout([$class: &#x27;GitSCM&#x27;, branches: [[name: &#x27;$&#123;branch_tag&#125;&#x27;]], extensions: [], userRemoteConfigs: [[url: &#x27;http://211.87.224.233:8929/sdu-weblab/springboot-helloworld.git&#x27;]]])</span><br></pre></td></tr></table></figure>
<p>其中<code>branch_tag</code>为git参数（此项功能需要 Git Parameter 插件）。</p>
<p>stage为：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">stage(&#x27;Stage 1: Fetch code from git&#x27;) &#123;</span><br><span class="line">    steps &#123;</span><br><span class="line">        checkout([$class: &#x27;GitSCM&#x27;, branches: [[name: &#x27;$&#123;branch_tag&#125;&#x27;]], extensions: [], userRemoteConfigs: [[url: &#x27;http://211.87.224.233:8929/sdu-weblab/springboot-helloworld.git&#x27;]]])</span><br><span class="line">        echo &#x27;Stage 1: Fetch code from git -- SUCCESS&#x27;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h3 id="Git-Parameter"><a href="#Git-Parameter" class="headerlink" title="Git Parameter"></a>Git Parameter</h3><p>该插件允许您在构建中分配<strong>branch, tag, pull request or revision number</strong>作为参数。</p>
<p>此插件将从你的项目中读取 GIT SCM 配置。<br>这个插件直接使用了<a target="_blank" rel="noopener" href="https://plugins.jenkins.io/git/">Git Plugin</a>和<a target="_blank" rel="noopener" href="https://plugins.jenkins.io/git-client/">Git Client Plugin</a>。</p>
<p>git 拉取分支：</p>
<p>checkout: Check out from version control</p>
<p><a target="_blank" rel="noopener" href="http://211.87.224.233:18080/">http://211.87.224.233:18080/</a></p>
<p><a target="_blank" rel="noopener" href="https://plugins.jenkins.io/git-parameter">https://plugins.jenkins.io/git-parameter</a></p>
<h3 id="使用WebHooks实现push分支自动build"><a href="#使用WebHooks实现push分支自动build" class="headerlink" title="使用WebHooks实现push分支自动build"></a>使用WebHooks实现push分支自动build</h3><p><del><a target="_blank" rel="noopener" href="http://211.87.224.233:8080/project/pipeline-springboot-helloworld">http://211.87.224.233:8080/project/pipeline-springboot-helloworld</a></del></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Hook executed successfully but returned HTTP 404 &lt;!doctype html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;&lt;title&gt;HTTP Status 404 – Not Found&lt;/title&gt;&lt;style type=&quot;text/css&quot;&gt;body &#123;font-family:Tahoma,Arial,sans-serif;&#125; h1, h2, h3, b &#123;color:white;background-color:#525D76;&#125; h1 &#123;font-size:22px;&#125; h2 &#123;font-size:16px;&#125; h3 &#123;font-size:14px;&#125; p &#123;font-size:12px;&#125; a &#123;color:black;&#125; .line &#123;height:1px;background-color:#525D76;border:none;&#125;&lt;/style&gt;&lt;/head&gt;&lt;body&gt;&lt;h1&gt;HTTP Status 404 – Not Found&lt;/h1&gt;&lt;/body&gt;&lt;/html&gt;</span><br></pre></td></tr></table></figure>



<ol>
<li>下载插件<strong>Gitlab</strong>插件</li>
<li>在gitlab上设置Jenkins里插件生成的webhook，并添加secret token。此次webhook token为<a target="_blank" rel="noopener" href="http://jenkins:8080/project/pipeline-springboot-helloworld">http://jenkins:8080/project/pipeline-springboot-helloworld</a></li>
</ol>
<h2 id="使用Jenkins容器内的Maven打包"><a href="#使用Jenkins容器内的Maven打包" class="headerlink" title="使用Jenkins容器内的Maven打包"></a>使用Jenkins容器内的Maven打包</h2><p>打包命令</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">$</span><span class="language-bash">&#123;MAVEN_HOME&#125;/bin/mvn clean package -Dmaven.test.skip=<span class="literal">true</span> <span class="comment"># 容器内的$&#123;MAVEN_HOME&#125;,不是宿主机的,在这里为/opt/maven</span></span></span><br></pre></td></tr></table></figure>



<p>片段生成器生成的语句:</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sh &#x27;/opt/maven/bin/mvn clean package -Dmaven.test.skip=true&#x27;</span><br></pre></td></tr></table></figure>





<p>stage为：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">stage(&#x27;Stage 2: Build the project using maven&#x27;) &#123;</span><br><span class="line">    steps &#123;</span><br><span class="line">        // 下面两条shell作debug用，后面删去</span><br><span class="line">        sh &#x27;pwd&#x27;</span><br><span class="line">        sh &#x27;ls -al&#x27;</span><br><span class="line">        sh &#x27;/opt/maven/bin/mvn clean package -Dmaven.test.skip=true&#x27;</span><br><span class="line">        echo &#x27;Stage 2: Build the project using maven -- SUCCESS&#x27;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>日志：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line">+ pwd</span><br><span class="line">/var/jenkins_home/workspace/pipeline-springboot-helloworld</span><br><span class="line">[Pipeline] sh</span><br><span class="line">+ ls -al</span><br><span class="line">total 8</span><br><span class="line">drwxr-xr-x 5 jenkins jenkins  100 May  6 10:11 .</span><br><span class="line">drwxr-xr-x 4 jenkins jenkins   98 May  6 10:09 ..</span><br><span class="line">drwxr-xr-x 8 jenkins jenkins  210 May  6 10:13 .git</span><br><span class="line">-rw-r--r-- 1 jenkins jenkins  387 May  5 15:35 .gitignore</span><br><span class="line">-rw-r--r-- 1 jenkins jenkins 1437 May  5 15:35 pom.xml</span><br><span class="line">drwxr-xr-x 4 jenkins jenkins   42 May  5 15:35 src</span><br><span class="line">drwxr-xr-x 6 jenkins jenkins  199 May  6 10:12 target</span><br><span class="line">[Pipeline] sh</span><br><span class="line">+ /opt/maven/bin/mvn clean package -Dmaven.test.skip=true</span><br><span class="line">[INFO] Scanning for projects...</span><br><span class="line">[INFO] </span><br><span class="line">[INFO] -----------------------&lt; com.sduweb:helloworld &gt;------------------------</span><br><span class="line">[INFO] Building helloworld 0.0.1-SNAPSHOT</span><br><span class="line">[INFO] --------------------------------[ jar ]---------------------------------</span><br><span class="line">[INFO] </span><br><span class="line">[INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ helloworld ---</span><br><span class="line">[INFO] Deleting /var/jenkins_home/workspace/pipeline-springboot-helloworld/target</span><br><span class="line">[INFO] </span><br><span class="line">[INFO] --- maven-resources-plugin:3.2.0:resources (default-resources) @ helloworld ---</span><br><span class="line">[INFO] Using &#x27;UTF-8&#x27; encoding to copy filtered resources.</span><br><span class="line">[INFO] Using &#x27;UTF-8&#x27; encoding to copy filtered properties files.</span><br><span class="line">[INFO] Copying 1 resource</span><br><span class="line">[INFO] Copying 0 resource</span><br><span class="line">[INFO] </span><br><span class="line">[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ helloworld ---</span><br><span class="line">[INFO] Changes detected - recompiling the module!</span><br><span class="line">[INFO] Compiling 2 source files to /var/jenkins_home/workspace/pipeline-springboot-helloworld/target/classes</span><br><span class="line">[INFO] </span><br><span class="line">[INFO] --- maven-resources-plugin:3.2.0:testResources (default-testResources) @ helloworld ---</span><br><span class="line">[INFO] Not copying test resources</span><br><span class="line">[INFO] </span><br><span class="line">[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ helloworld ---</span><br><span class="line">[INFO] Not compiling test sources</span><br><span class="line">[INFO] </span><br><span class="line">[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ helloworld ---</span><br><span class="line">[INFO] Tests are skipped.</span><br><span class="line">[INFO] </span><br><span class="line">[INFO] --- maven-jar-plugin:3.2.2:jar (default-jar) @ helloworld ---</span><br><span class="line">[INFO] Building jar: /var/jenkins_home/workspace/pipeline-springboot-helloworld/target/helloworld-0.0.1-SNAPSHOT.jar</span><br><span class="line">[INFO] </span><br><span class="line">[INFO] --- spring-boot-maven-plugin:2.6.4:repackage (repackage) @ helloworld ---</span><br><span class="line">[INFO] Replacing main artifact with repackaged archive</span><br><span class="line">[INFO] ------------------------------------------------------------------------</span><br><span class="line">[INFO] BUILD SUCCESS</span><br><span class="line">[INFO] ------------------------------------------------------------------------</span><br><span class="line">[INFO] Total time:  2.425 s</span><br><span class="line">[INFO] Finished at: 2022-05-06T10:14:04Z</span><br><span class="line">[INFO] ------------------------------------------------------------------------</span><br></pre></td></tr></table></figure>


<p>从上面的日志输出可以得到如下结论：</p>
<ol>
<li>对于Jenkins每一个流水线项目，Jenkins都会在存储在<code>/var/jenkins_home/workspace/$&#123;projectName&#125;</code></li>
<li>从gitlab拉取代码后存储在本流水线对应的目录下<code>/var/jenkins_home/workspace/$&#123;projectName&#125;</code></li>
<li>打完jar后存储在<code>/var/jenkins_home/workspace/$&#123;projectName&#125;/target/xxx.jar</code></li>
</ol>
<blockquote>
<p><strong>跳过单元测试</strong></p>
<ul>
<li><p>-DskipTests，不执行<a target="_blank" rel="noopener" href="https://so.csdn.net/so/search?q=%E6%B5%8B%E8%AF%95%E7%94%A8%E4%BE%8B&spm=1001.2101.3001.7020">测试用例</a>，但编译测试用例类生成相应的class文件至target&#x2F;test-classes下。 </p>
</li>
<li><p>-Dmaven.test.skip&#x3D;true，不执行测试用例，也不编译测试用例类。</p>
</li>
</ul>
</blockquote>
<blockquote>
<p>参考<br><a target="_blank" rel="noopener" href="https://www.jianshu.com/p/6f91752f3962">https://www.jianshu.com/p/6f91752f3962</a><br><a target="_blank" rel="noopener" href="https://www.jianshu.com/p/6f57c322e50e">https://www.jianshu.com/p/6f57c322e50e</a></p>
</blockquote>
<h2 id="使用Jenkins容器内的Docker制作自定义镜像"><a href="#使用Jenkins容器内的Docker制作自定义镜像" class="headerlink" title="使用Jenkins容器内的Docker制作自定义镜像"></a>使用Jenkins容器内的Docker制作自定义镜像</h2><p>pwd: &#x2F;var&#x2F;jenkins_home&#x2F;workspace&#x2F;${projectName}<br>&#x2F;target&#x2F;xxx.jar</p>
<p>Dockerfile</p>
<figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> openjdk:<span class="number">8</span>-jdk-alpine</span><br><span class="line"><span class="keyword">ARG</span> JarFileName</span><br><span class="line"><span class="keyword">COPY</span><span class="language-bash"> <span class="variable">$&#123;JarFileName&#125;</span> /opt/weblab/</span></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="language-bash"> /opt/weblab/</span></span><br><span class="line"><span class="keyword">CMD</span><span class="language-bash"> java -jar <span class="variable">$&#123;JarFileName&#125;</span></span></span><br></pre></td></tr></table></figure>



<p><del>执行shell</del></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">cp -r /opt/docker_workdir ./</span><br><span class="line">cp ./target/*.jar ./docker_workdir</span><br><span class="line">docker build -t $&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125; ./docker_workdir --build-arg JarFileName=$(ls target -l | grep jar$ | awk &#x27;&#123;print $9&#125;&#x27;)</span><br></pre></td></tr></table></figure>

<p><del>流水线片段</del></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">sh &#x27;&#x27;&#x27;cp -r /opt/docker_workdir ./</span><br><span class="line">cp ./target/*.jar ./docker_workdir</span><br><span class="line">docker build -t $&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125; ./docker_workdir --build-arg JarFileName=$(ls target -l | grep jar$ | awk \&#x27;&#123;print $9&#125;\&#x27;)&#x27;&#x27;&#x27;</span><br></pre></td></tr></table></figure>



<p><del>stage: 为</del></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">stage(&#x27;Stage 3: Make a custom image using docker&#x27;) &#123;</span><br><span class="line">    steps &#123;</span><br><span class="line">        sh &#x27;&#x27;&#x27;</span><br><span class="line">        cp -r /opt/docker_workdir ./</span><br><span class="line">        cp ./target/*.jar ./docker_workdir</span><br><span class="line">        docker build -t $&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125; ./docker_workdir --build-arg JarFileName=$(ls target -l | grep jar$ | awk \&#x27;&#123;print $9&#125;\&#x27;)</span><br><span class="line">        &#x27;&#x27;&#x27;</span><br><span class="line">        echo &#x27;Stage 3: Make a custom image using docker -- SUCCESS&#x27;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>







<p>build_image.sh</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">cp -r /opt/docker_workdir ./</span><br><span class="line">cp ./target/*.jar ./docker_workdir</span><br><span class="line">jarFileName=$(ls *.jar)</span><br><span class="line">echo &quot;jar is $&#123;jarFileName&#125;&quot;</span><br><span class="line">docker build -t $&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125; ./docker_workdir --build-arg  JarFileName=&quot;$&#123;jarFileName&#125;&quot;</span><br></pre></td></tr></table></figure>











<h2 id="将自定义镜像推送到Harbor"><a href="#将自定义镜像推送到Harbor" class="headerlink" title="将自定义镜像推送到Harbor"></a>将自定义镜像推送到Harbor</h2><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker login -u admin -p admin $HARBOR_URL</span><br></pre></td></tr></table></figure>



<p>Jenkinsfile:</p>
<p>增加以下环境变量</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">pipeline &#123;</span><br><span class="line">    agent any</span><br><span class="line">    </span><br><span class="line">		environment &#123;</span><br><span class="line">        harbor_user = &#x27;admin&#x27;</span><br><span class="line">        harbor_password = &#x27;admin&#x27;</span><br><span class="line">        harbor_url = &#x27;211.87.224.233:8930&#x27;</span><br><span class="line">        harbor_repo = &#x27;sdu-weblab&#x27;</span><br><span class="line">    &#125;</span><br><span class="line">    // ......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>Shell</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">docker login -u $&#123;harborUser&#125; -p $&#123;harborPassword&#125; $&#123;harbor_url&#125;</span><br><span class="line">docker tag $&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125; $&#123;harbor_url&#125;/$&#123;harbor_repo&#125;/$&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125;</span><br><span class="line">docker rmi $(docker images -f &quot;dangling=true&quot; -q) -f</span><br><span class="line">docker push $&#123;harbor_url&#125;/$&#123;harbor_repo&#125;/$&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125;</span><br></pre></td></tr></table></figure>



<p><del>pipeline script:</del></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">sh &#x27;&#x27;&#x27;</span><br><span class="line">    docker login -u $&#123;harborUser&#125; -p $&#123;harborPassword&#125; $&#123;harbor_url&#125;</span><br><span class="line">    docker tag $&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125; $&#123;harbor_url&#125;/$&#123;harbor_repo&#125;/$&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125;</span><br><span class="line">    docker rmi $(docker images -f &quot;dangling=true&quot; -q) -f</span><br><span class="line">    docker push $&#123;harbor_url&#125;/$&#123;harbor_repo&#125;/$&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125;</span><br><span class="line"> &#x27;&#x27;&#x27;</span><br></pre></td></tr></table></figure>



<p><del>stage为：</del></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">stage(&#x27;Stage 4: Push image to Harbor&#x27;) &#123;</span><br><span class="line">    steps &#123;</span><br><span class="line">        sh &#x27;&#x27;&#x27;</span><br><span class="line">            docker login -u $&#123;harborUser&#125; -p $&#123;harborPassword&#125; $&#123;harbor_url&#125;</span><br><span class="line">            docker tag $&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125; $&#123;harbor_url&#125;/$&#123;harbor_repo&#125;/$&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125;</span><br><span class="line">            docker rmi $(docker images -f &quot;dangling=true&quot; -q) -f</span><br><span class="line">            docker push $&#123;harbor_url&#125;/$&#123;harbor_repo&#125;/$&#123;JOB_NAME&#125;:$&#123;branch_tag##*/&#125;</span><br><span class="line">         &#x27;&#x27;&#x27;</span><br><span class="line">        echo &#x27;Stage 4: Push image to Harbor -- SUCCESS&#x27;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><del>改为执行本地的脚本。</del></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">harbor_user=$1</span><br><span class="line">harbor_password=$2</span><br><span class="line">harbor_url=$3</span><br><span class="line">harbor_repo=$4</span><br><span class="line">project=$5</span><br><span class="line">branch_tag=$6</span><br><span class="line"></span><br><span class="line">image_name=$&#123;harbor_url&#125;/$&#123;harbor_repo&#125;/$&#123;project&#125;:$&#123;branch_tag##*/&#125;</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">删除和镜像关联的容器(正常情况不会有，但这里是单机部署)</span></span><br><span class="line">container_id=$(docker ps -a | grep &quot;$&#123;image_name&#125;&quot; | awk &#x27;&#123;print $1&#125;&#x27;)</span><br><span class="line">if [ &quot;$&#123;container_id&#125;&quot; != &quot;&quot; ] ; then</span><br><span class="line">  echo &quot;Container $&#123;container_id&#125; of $&#123;image_name&#125; is running, now try to kill it...&quot;</span><br><span class="line">  docker stop &quot;$container_id&quot;</span><br><span class="line">  docker rm &quot;$container_id&quot;</span><br><span class="line">fi</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">todo 这样做其实还有是问题的，就是上面的 container_id 可能会有多个</span></span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">shellcheck <span class="built_in">disable</span>=SC2086</span></span><br><span class="line">docker login -u &quot;$&#123;harbor_user&#125;&quot; -p &quot;$&#123;harbor_password&#125;&quot; $&#123;harbor_url&#125;</span><br><span class="line"></span><br><span class="line">docker tag &quot;$&#123;project&#125;&quot;:&quot;$&#123;branch_tag##*/&#125;&quot; &quot;$&#123;image_name&#125;&quot;</span><br><span class="line">docker rmi $(docker images -f &quot;dangling=true&quot; -q) -f</span><br><span class="line">docker push image_name</span><br></pre></td></tr></table></figure>



<p>Jenkins端shell：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/opt/docker_workdir/push_to_harbor.sh $harbor_user $harbor_password $harbor_url $harbor_repo $JOB_NAME $branch_tag</span><br></pre></td></tr></table></figure>

<p>流水线片段</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sh &#x27;/opt/docker_workdir/push_to_harbor.sh $harbor_user $harbor_password $harbor_url $harbor_repo $JOB_NAME $branch_tag&#x27;</span><br></pre></td></tr></table></figure>







<p>其中：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">删除悬挂镜像</span></span><br><span class="line">docker rmi $(docker images -f &quot;dangling=true&quot; -q) </span><br></pre></td></tr></table></figure>

<blockquote>
<p><em>docker中的none:none镜像是怎么回事？</em><br><a target="_blank" rel="noopener" href="https://projectatomic.io/blog/2015/07/what-are-docker-none-none-images/">https://projectatomic.io/blog/2015/07/what-are-docker-none-none-images/</a></p>
</blockquote>
<p>用 go 改写：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">	<span class="string">&quot;context&quot;</span></span><br><span class="line">	<span class="string">&quot;flag&quot;</span></span><br><span class="line">	<span class="string">&quot;fmt&quot;</span></span><br><span class="line">	<span class="string">&quot;io&quot;</span></span><br><span class="line">	<span class="string">&quot;log&quot;</span></span><br><span class="line">	<span class="string">&quot;os&quot;</span></span><br><span class="line">	<span class="string">&quot;strings&quot;</span></span><br><span class="line">	<span class="string">&quot;time&quot;</span></span><br><span class="line"></span><br><span class="line">	<span class="string">&quot;github.com/docker/docker/api/types&quot;</span></span><br><span class="line">	<span class="string">&quot;github.com/docker/docker/client&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line">	harborUser     <span class="type">string</span></span><br><span class="line">	harborPassword <span class="type">string</span></span><br><span class="line">	harborUrl      <span class="type">string</span></span><br><span class="line">	harborRepo     <span class="type">string</span></span><br><span class="line">	project        <span class="type">string</span></span><br><span class="line">	version        <span class="type">string</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line">	logFileName <span class="type">string</span></span><br><span class="line">	logFile     *os.File</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">init</span><span class="params">()</span></span> &#123;</span><br><span class="line">	<span class="keyword">var</span> err <span class="type">error</span></span><br><span class="line">	logFileName = time.Now().Format(<span class="string">&quot;2006-01-02 15:04:05&quot;</span>) + <span class="string">&quot;[push_to_harbor].log&quot;</span></span><br><span class="line">	logFile, err = os.OpenFile(logFileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, <span class="number">0644</span>)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		<span class="built_in">panic</span>(err)</span><br><span class="line">	&#125;</span><br><span class="line">	log.SetOutput(logFile)</span><br><span class="line">	log.SetPrefix(<span class="string">&quot;[sdu-weblab-deploy]&quot;</span>)</span><br><span class="line">	log.SetFlags(log.LstdFlags | log.Lshortfile | log.LUTC)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	flag.StringVar(&amp;harborUser, <span class="string">&quot;harbor_User&quot;</span>, <span class="string">&quot;&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.StringVar(&amp;harborPassword, <span class="string">&quot;harbor_password&quot;</span>, <span class="string">&quot;&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.StringVar(&amp;harborUrl, <span class="string">&quot;harbor_url&quot;</span>, <span class="string">&quot;&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.StringVar(&amp;harborRepo, <span class="string">&quot;harbor_repo&quot;</span>, <span class="string">&quot;&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.StringVar(&amp;project, <span class="string">&quot;project&quot;</span>, <span class="string">&quot;&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.StringVar(&amp;version, <span class="string">&quot;version&quot;</span>, <span class="string">&quot;&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	<span class="keyword">if</span> <span class="built_in">len</span>(harborUser) == <span class="number">0</span> || <span class="built_in">len</span>(harborPassword) == <span class="number">0</span> || <span class="built_in">len</span>(harborUrl) == <span class="number">0</span> ||</span><br><span class="line">		<span class="built_in">len</span>(harborRepo) == <span class="number">0</span> || <span class="built_in">len</span>(project) == <span class="number">0</span> || <span class="built_in">len</span>(version) == <span class="number">0</span> &#123;</span><br><span class="line">		log.Fatal(<span class="string">&quot;key args missing&quot;</span>)</span><br><span class="line">	&#125;</span><br><span class="line">	log.Printf(<span class="string">&quot;Notice: harbor_url=%s, harbor_repo=%s, project=%s, version=%s&quot;</span>,</span><br><span class="line">		harborUrl, harborRepo, project, version)</span><br><span class="line"></span><br><span class="line">	ctx := context.Background()</span><br><span class="line">	cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;New docker client fail, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	version = getRealVersion(version)</span><br><span class="line">	imageName := getHarborImageName(harborUrl, harborRepo, project, version)</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 删除和镜像关联的容器(正常情况不会有，但这里是单机部署)</span></span><br><span class="line">	<span class="comment">// 1. check existing containers and remove it if so</span></span><br><span class="line">	log.Printf(<span class="string">&quot;Check if container of %s exists...\n&quot;</span>, imageName)</span><br><span class="line">	containers, err := cli.ContainerList(ctx, types.ContainerListOptions&#123;All: <span class="literal">true</span>&#125;)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;List all containers fail, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">var</span> timeOut = time.Second</span><br><span class="line">	<span class="keyword">for</span> _, c := <span class="keyword">range</span> containers &#123;</span><br><span class="line">		<span class="keyword">if</span> c.Image == imageName &#123;</span><br><span class="line">			cid := c.ID</span><br><span class="line">			err = cli.ContainerStop(ctx, cid, &amp;timeOut)</span><br><span class="line">			<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">				log.Printf(<span class="string">&quot;Warning: container.ID=%s stop failed&quot;</span>, cid)</span><br><span class="line">			&#125;</span><br><span class="line">			err = cli.ContainerRemove(ctx, cid, types.ContainerRemoveOptions&#123;Force: <span class="literal">true</span>&#125;)</span><br><span class="line">			<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">				log.Fatalf(<span class="string">&quot;Container.ID=%s remove failed&quot;</span>, cid)</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 2. tag by the rule of harbor</span></span><br><span class="line">	<span class="comment">// 2.1 check and remove outdated version images</span></span><br><span class="line">	log.Printf(<span class="string">&quot;Check if image %s exists...\n&quot;</span>, imageName)</span><br><span class="line">	imageList, err := cli.ImageList(ctx, types.ImageListOptions&#123;&#125;)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;List all images fail, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">for</span> _, image := <span class="keyword">range</span> imageList &#123;</span><br><span class="line">		<span class="keyword">for</span> _, repoTag := <span class="keyword">range</span> image.RepoTags &#123;</span><br><span class="line">			<span class="keyword">if</span> repoTag == imageName &#123;</span><br><span class="line">				log.Printf(<span class="string">&quot;Image=%s exists, next to remove it&quot;</span>, image.ID)</span><br><span class="line">				cli.ImageRemove(ctx, image.ID, types.ImageRemoveOptions&#123;</span><br><span class="line">					Force: <span class="literal">true</span>,</span><br><span class="line">				&#125;)</span><br><span class="line">				<span class="keyword">break</span></span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">// 2.2 tag</span></span><br><span class="line">	sourceImageName := getSourceImageName(harborRepo, project, version)</span><br><span class="line">	err = cli.ImageTag(ctx, sourceImageName, imageName)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;image tag fail, err=%v&quot;</span>, err.Error())</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="comment">// todo docker rmi $(docker images -f &quot;dangling=true&quot; -q) -f</span></span><br><span class="line"></span><br><span class="line">	<span class="comment">// 3. push image to harbor</span></span><br><span class="line">	log.Printf(<span class="string">&quot;Push image %s to Harbor...\n&quot;</span>, imageName)</span><br><span class="line">	_, err = cli.RegistryLogin(ctx, types.AuthConfig&#123;</span><br><span class="line">		<span class="comment">// todo security</span></span><br><span class="line">		Username:      <span class="string">&quot;admin&quot;</span>,</span><br><span class="line">		Password:      <span class="string">&quot;admin&quot;</span>,</span><br><span class="line">		ServerAddress: harborUrl,</span><br><span class="line">	&#125;)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;push to harbor fail, err=%v&quot;</span>, err.Error())</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	out, err := cli.ImagePush(ctx, imageName, types.ImagePushOptions&#123;&#125;)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;Pulling image failed, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">defer</span> out.Close()</span><br><span class="line">	io.Copy(logFile, out)</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// remove possible &quot;/&quot;</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">getRealVersion</span><span class="params">(rawVersion <span class="type">string</span>)</span></span> <span class="type">string</span> &#123;</span><br><span class="line">	parts := strings.Split(rawVersion, <span class="string">&quot;/&quot;</span>)</span><br><span class="line">	<span class="keyword">if</span> <span class="built_in">len</span>(parts) &gt; <span class="number">0</span> &#123;</span><br><span class="line">		<span class="keyword">return</span> parts[<span class="built_in">len</span>(parts)<span class="number">-1</span>]</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">return</span> version</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">getSourceImageName</span><span class="params">(team, project, version <span class="type">string</span>)</span></span> <span class="type">string</span> &#123;</span><br><span class="line">	<span class="keyword">return</span> fmt.Sprintf(<span class="string">&quot;%s/%s/%s:%s&quot;</span>, team, harborRepo, project, version)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">getHarborImageName</span><span class="params">(harborUrl, harborRepo, project, version <span class="type">string</span>)</span></span> <span class="type">string</span> &#123;</span><br><span class="line">	<span class="keyword">return</span> fmt.Sprintf(<span class="string">&quot;%s/%s/%s:%s&quot;</span>, harborUrl, harborRepo, project, version)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h2 id="通过SSH通知目标服务器部署"><a href="#通过SSH通知目标服务器部署" class="headerlink" title="通过SSH通知目标服务器部署"></a>通过SSH通知目标服务器部署</h2><p>这里使用<strong>Publish Over SSH</strong>。</p>
<p><strong>部署端</strong>的执行脚本：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line">harbor_url=$1</span><br><span class="line">harbor_repo=$2</span><br><span class="line">project=$3</span><br><span class="line">version=$4</span><br><span class="line">container_port=$5</span><br><span class="line"></span><br><span class="line">image_name=$harbor_url/$harbor_repo/$project:$version</span><br><span class="line"></span><br><span class="line">container_id=$(docker ps -a | grep &quot;$&#123;image_name&#125;&quot; | awk &#x27;&#123;print $1&#125;&#x27;)</span><br><span class="line"></span><br><span class="line">echo &quot;Check if container of $&#123;image_name&#125; exists...&quot;</span><br><span class="line">if [ &quot;$&#123;container_id&#125;&quot; != &quot;&quot; ] ; then</span><br><span class="line">  echo &quot;Container $&#123;container_id&#125; of $&#123;image_name&#125; is running, now try to kill it...&quot;</span><br><span class="line">  docker stop &quot;$container_id&quot;</span><br><span class="line">  docker rm &quot;$container_id&quot;</span><br><span class="line">fi</span><br><span class="line"></span><br><span class="line">old_image=$(docker images | grep &quot;$&#123;image_name&#125;&quot;)</span><br><span class="line"></span><br><span class="line">if [ &quot;$&#123;old_image&#125;&quot; != &quot;&quot; ] ; then</span><br><span class="line">  echo &quot;Image $image_name exists, now try to remove it&quot;</span><br><span class="line">  docker rmi &quot;$image_name&quot; -f</span><br><span class="line">fi</span><br><span class="line"></span><br><span class="line">echo &quot;Try to login Harbor...&quot;</span><br><span class="line">docker login -u amdin -p admin &quot;$harbor_url&quot;</span><br><span class="line">echo &quot;Login Harbor successfully.&quot;</span><br><span class="line"></span><br><span class="line">echo &quot;Try to pull $image_name from Harbor...&quot;</span><br><span class="line">docker pull &quot;$&#123;image_name&#125;&quot;</span><br><span class="line">echo &quot;Pull image $&#123;image_name&#125; from Harbor successfully.&quot;</span><br><span class="line"></span><br><span class="line">docker run -d -p &quot;$container_port&quot; --name &quot;$harbor_repo.$project.$version&quot; &quot;$image_name&quot;</span><br><span class="line"></span><br><span class="line">echo &quot;Deploy service successfully.&quot;</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>以上脚本执行了如下事情：</p>
<ol>
<li>检查旧版本的容器是否在运行，如果有停止并删除；</li>
<li>检查旧镜像是否存在，如果有删除；</li>
<li>登录私服Harbor</li>
<li>拉取最新的镜像</li>
<li>运行容器</li>
</ol>
<p><strong>发布端（Jenkins）</strong>的执行脚本</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cd deploy &amp;&amp; touch deploy.log &amp;&amp; /home/jsy/devops/docker_workdir/deploy.sh $harbor_url $harbor_repo $JOB_NAME $branch_tag $container_port &gt; deploy.log</span><br></pre></td></tr></table></figure>



<p>测试一下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cd deploy &amp;&amp; touch deploy.log &amp;&amp; /home/jsy/devops/docker_workdir/deploy.sh 211.87.224.233:8930 sdu-weblab springboot-helloworld origin/master 8080 &gt; deploy.log</span><br></pre></td></tr></table></figure>





<p>使用sshPublisher生成流水线脚本：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sshPublisher(publishers: [sshPublisherDesc(configName: &#x27;sdu-weblab-local-machine&#x27;, transfers: [sshTransfer(cleanRemote: false, excludes: &#x27;&#x27;, execCommand: &quot;cd deploy &amp;&amp; touch deploy.log &amp;&amp; /home/jsy/devops/docker_workdir/deploy.sh $harbor_url $harbor_repo $JOB_NAME $branch_tag $container_port &gt; deploy.log&quot;, execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: &#x27;[, ]+&#x27;, remoteDirectory: &#x27;&#x27;, remoteDirectorySDF: false, removePrefix: &#x27;&#x27;, sourceFiles: &#x27;&#x27;)], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])</span><br></pre></td></tr></table></figure>



<p>stage为</p>
<pre><code>        stage(&#39;Stage 5: Publish over SSH&#39;) &#123;
            steps &#123;
                sshPublisher(publishers: [sshPublisherDesc(configName: &#39;sdu-weblab-local-machine&#39;, transfers: [sshTransfer(cleanRemote: false, excludes: &#39;&#39;, execCommand: &quot;cd deploy &amp;&amp; touch deploy.log &amp;&amp; /home/jsy/devops/docker_workdir/deploy.sh $harbor_url $harbor_repo $JOB_NAME $branch_tag $container_port &gt; deploy.log&quot;, execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: &#39;[, ]+&#39;, remoteDirectory: &#39;&#39;, remoteDirectorySDF: false, removePrefix: &#39;&#39;, sourceFiles: &#39;&#39;)], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
                echo &#39;Stage 5: Publish over SSH -- SUCCESS&#39;
            &#125;
        &#125;
</code></pre>
<h2 id="通过SSH通知目标服务器部署（部署端使用go部署）"><a href="#通过SSH通知目标服务器部署（部署端使用go部署）" class="headerlink" title="通过SSH通知目标服务器部署（部署端使用go部署）"></a>通过SSH通知目标服务器部署（部署端使用go部署）</h2><h3 id="部署程序"><a href="#部署程序" class="headerlink" title="部署程序"></a>部署程序</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">	<span class="string">&quot;context&quot;</span></span><br><span class="line">	<span class="string">&quot;errors&quot;</span></span><br><span class="line">	<span class="string">&quot;flag&quot;</span></span><br><span class="line">	<span class="string">&quot;fmt&quot;</span></span><br><span class="line">	<span class="string">&quot;io&quot;</span></span><br><span class="line">	<span class="string">&quot;log&quot;</span></span><br><span class="line">	<span class="string">&quot;os&quot;</span></span><br><span class="line">	<span class="string">&quot;os/exec&quot;</span></span><br><span class="line">	<span class="string">&quot;strconv&quot;</span></span><br><span class="line">	<span class="string">&quot;strings&quot;</span></span><br><span class="line">	<span class="string">&quot;time&quot;</span></span><br><span class="line"></span><br><span class="line">	<span class="string">&quot;github.com/docker/docker/api/types&quot;</span></span><br><span class="line">	<span class="string">&quot;github.com/docker/docker/api/types/container&quot;</span></span><br><span class="line">	<span class="string">&quot;github.com/docker/docker/client&quot;</span></span><br><span class="line">	<span class="string">&quot;github.com/docker/go-connections/nat&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> (</span><br><span class="line">	DefaultVersion       = <span class="string">&quot;master&quot;</span></span><br><span class="line">	MonitorProgrammeName = <span class="string">&quot;monitor&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line">	harborUrl     <span class="type">string</span></span><br><span class="line">	harborRepo    <span class="type">string</span></span><br><span class="line">	project       <span class="type">string</span></span><br><span class="line">	version       <span class="type">string</span></span><br><span class="line">	containerPort <span class="type">int</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line">	logFileName <span class="type">string</span></span><br><span class="line">	containerID <span class="type">string</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">init</span><span class="params">()</span></span> &#123;</span><br><span class="line">	logFileName = time.Now().Format(<span class="string">&quot;2006-01-02 15:04:05&quot;</span>) + <span class="string">&quot;.log&quot;</span></span><br><span class="line">	logFile, err := os.OpenFile(logFileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, <span class="number">0644</span>)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		<span class="built_in">panic</span>(err)</span><br><span class="line">	&#125;</span><br><span class="line">	log.SetOutput(logFile)</span><br><span class="line">	log.SetPrefix(<span class="string">&quot;[sdu-weblab-deploy]&quot;</span>)</span><br><span class="line">	log.SetFlags(log.LstdFlags | log.Lshortfile | log.LUTC)</span><br><span class="line">	<span class="keyword">return</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	flag.StringVar(&amp;harborUrl, <span class="string">&quot;harbor_url&quot;</span>, <span class="string">&quot;&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.StringVar(&amp;harborRepo, <span class="string">&quot;harbor_repo&quot;</span>, <span class="string">&quot;&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.StringVar(&amp;project, <span class="string">&quot;project&quot;</span>, <span class="string">&quot;&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.StringVar(&amp;version, <span class="string">&quot;version&quot;</span>, DefaultVersion, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.IntVar(&amp;containerPort, <span class="string">&quot;container_port&quot;</span>, <span class="number">8080</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.Parse()</span><br><span class="line">	<span class="keyword">if</span> <span class="built_in">len</span>(harborUrl) == <span class="number">0</span> || <span class="built_in">len</span>(harborRepo) == <span class="number">0</span> || <span class="built_in">len</span>(project) == <span class="number">0</span> &#123;</span><br><span class="line">		log.Fatal(<span class="string">&quot;key args missing&quot;</span>)</span><br><span class="line">	&#125;</span><br><span class="line">	log.Printf(<span class="string">&quot;Notice: harbor_url=%s, harbor_repo=%s, project=%s, version=%s, container_port=%d&quot;</span>,</span><br><span class="line">		harborUrl, harborRepo, project, version, containerPort)</span><br><span class="line"></span><br><span class="line">	version = getBranch(version)</span><br><span class="line">	imageName := harborUrl + <span class="string">&quot;/&quot;</span> + harborRepo + <span class="string">&quot;/&quot;</span> + project + <span class="string">&quot;:&quot;</span> + version</span><br><span class="line">	log.Printf(<span class="string">&quot;Docker image name is %s&quot;</span>, imageName)</span><br><span class="line"></span><br><span class="line">	ctx := context.Background()</span><br><span class="line">	cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;New docker client fail, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 1. check existing containers</span></span><br><span class="line">	log.Printf(<span class="string">&quot;Check if container of %s exists...\n&quot;</span>, imageName)</span><br><span class="line">	containers, err := cli.ContainerList(ctx, types.ContainerListOptions&#123;All: <span class="literal">true</span>&#125;)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;List all containers fail, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="keyword">var</span> timeOut = time.Second</span><br><span class="line">	<span class="keyword">for</span> _, c := <span class="keyword">range</span> containers &#123;</span><br><span class="line">		<span class="keyword">if</span> c.Image == imageName &#123;</span><br><span class="line">			cid := c.ID</span><br><span class="line">			err = cli.ContainerStop(ctx, cid, &amp;timeOut)</span><br><span class="line">			<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">				log.Printf(<span class="string">&quot;Warning: container.ID=%s stop failed&quot;</span>, cid)</span><br><span class="line">			&#125;</span><br><span class="line">			err = cli.ContainerRemove(ctx, cid, types.ContainerRemoveOptions&#123;Force: <span class="literal">true</span>&#125;)</span><br><span class="line">			<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">				log.Fatalf(<span class="string">&quot;Container.ID=%s remove failed&quot;</span>, cid)</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 2. check outdated version images</span></span><br><span class="line">	log.Printf(<span class="string">&quot;Check if image %s exists...\n&quot;</span>, imageName)</span><br><span class="line">	imageList, err := cli.ImageList(ctx, types.ImageListOptions&#123;</span><br><span class="line">		All: <span class="literal">true</span>,</span><br><span class="line">	&#125;)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;List all images fail, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">for</span> _, image := <span class="keyword">range</span> imageList &#123;</span><br><span class="line">		<span class="keyword">for</span> _, repoTag := <span class="keyword">range</span> image.RepoTags &#123;</span><br><span class="line">			<span class="keyword">if</span> repoTag == imageName &#123;</span><br><span class="line">				cli.ImageRemove(ctx, image.ID, types.ImageRemoveOptions&#123;</span><br><span class="line">					Force: <span class="literal">true</span>,</span><br><span class="line">				&#125;)</span><br><span class="line">				<span class="keyword">break</span></span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 3. pull image from harbor</span></span><br><span class="line">	log.Printf(<span class="string">&quot;Pull image %s from Harbor...\n&quot;</span>, imageName)</span><br><span class="line">	_, err = cli.RegistryLogin(ctx, types.AuthConfig&#123;</span><br><span class="line">		<span class="comment">// todo security</span></span><br><span class="line">		Username:      <span class="string">&quot;admin&quot;</span>,</span><br><span class="line">		Password:      <span class="string">&quot;admin&quot;</span>,</span><br><span class="line">		ServerAddress: harborUrl,</span><br><span class="line">	&#125;)</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		<span class="built_in">panic</span>(<span class="literal">nil</span>)</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// out 是响应输出流，可以不输出</span></span><br><span class="line">	out, err := cli.ImagePull(ctx, imageName, types.ImagePullOptions&#123;&#125;)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		<span class="built_in">panic</span>(err)</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">defer</span> out.Close()</span><br><span class="line">	io.Copy(os.Stdout, out)</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 4. docker run -d -p &quot;$container_port&quot; --name &quot;$harbor_repo.$project.$version&quot; &quot;$image_name&quot;</span></span><br><span class="line">	log.Printf(<span class="string">&quot;Run image %s to be contaniner...\n&quot;</span>, imageName)</span><br><span class="line">	containerName := harborRepo + <span class="string">&quot;.&quot;</span> + project + <span class="string">&quot;.&quot;</span> + version</span><br><span class="line">	containerNatPort := nat.Port(strconv.Itoa(containerPort) + <span class="string">&quot;/tcp&quot;</span>)</span><br><span class="line"></span><br><span class="line">	resp, err := cli.ContainerCreate(ctx,</span><br><span class="line">		&amp;container.Config&#123;</span><br><span class="line">			Image: imageName,</span><br><span class="line">			ExposedPorts: nat.PortSet&#123;</span><br><span class="line">				containerNatPort: <span class="keyword">struct</span>&#123;&#125;&#123;&#125;,</span><br><span class="line">			&#125;,</span><br><span class="line">		&#125;, &amp;container.HostConfig&#123;</span><br><span class="line">			PublishAllPorts: <span class="literal">true</span>,</span><br><span class="line">		&#125;, <span class="literal">nil</span>, <span class="literal">nil</span>, containerName)</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;Creating container failed, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">if</span> err = cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions&#123;&#125;); err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;Starting container failed, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	containerID = resp.ID</span><br><span class="line">	log.Printf(<span class="string">&quot;Container ID = %s&quot;</span>, containerID)</span><br><span class="line"></span><br><span class="line">	<span class="comment">// todo register to center</span></span><br><span class="line">	hostPortOfContainerPort, err := getHostPortOfContainerPort(containerID, strconv.Itoa(containerPort))</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Printf(<span class="string">&quot;Error: get port binding failed, err=%v&quot;</span>, err)</span><br><span class="line">	&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">		log.Printf(<span class="string">&quot;Container hostPort of container port is %s&quot;</span>, hostPortOfContainerPort)</span><br><span class="line">	&#125;</span><br><span class="line">	log.Println(<span class="string">&quot;Deploy successfully.&quot;</span>)</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 5. run a new programme to fetch log stream and transfer it to websocket</span></span><br><span class="line">	runGoProgramme(MonitorProgrammeName)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">runGoProgramme</span><span class="params">(programme <span class="type">string</span>)</span></span> &#123;</span><br><span class="line">	log.Printf(<span class="string">&quot;Next to run programme %s &quot;</span>, programme)</span><br><span class="line">	sduWeblabBinHome := os.Getenv(<span class="string">&quot;SDU_WEBLAB_BIN_HOME&quot;</span>)</span><br><span class="line">	s := fmt.Sprintf(<span class="string">&quot;%s/%s --logFileName=%s --containerID=%s&quot;</span>, sduWeblabBinHome, programme, logFileName, containerID)</span><br><span class="line">	log.Printf(<span class="string">&quot;Shell script is %s&quot;</span>, s)</span><br><span class="line">	err := exec.Command(<span class="string">&quot;/bin/bash&quot;</span>, <span class="string">&quot;-c&quot;</span>, s).Start()</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;Fail to run programe %s, err=%v&quot;</span>, programme, err)</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">getBranch</span><span class="params">(repoBranch <span class="type">string</span>)</span></span> <span class="type">string</span> &#123;</span><br><span class="line">	parts := strings.Split(repoBranch, <span class="string">&quot;/&quot;</span>)</span><br><span class="line">	<span class="keyword">if</span> <span class="built_in">len</span>(parts) &gt; <span class="number">0</span> &#123;</span><br><span class="line">		<span class="keyword">return</span> parts[<span class="built_in">len</span>(parts)<span class="number">-1</span>]</span><br><span class="line">	&#125;</span><br><span class="line">	<span class="keyword">return</span> DefaultVersion</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">getHostPortOfContainerPort</span><span class="params">(containerId <span class="type">string</span>, containerPort <span class="type">string</span>)</span></span> (port <span class="type">string</span>, err <span class="type">error</span>) &#123;</span><br><span class="line">	shell := <span class="string">&quot;docker port &quot;</span> + containerId + <span class="string">&quot; | grep &quot;</span> + containerPort + <span class="string">&quot; | awk &#x27;&#123;print $3&#125;&#x27;&quot;</span></span><br><span class="line">	output, err := exec.Command(<span class="string">&quot;bash&quot;</span>, <span class="string">&quot;-c&quot;</span>, shell).Output()</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		<span class="keyword">return</span></span><br><span class="line">	&#125;</span><br><span class="line">	shellOut := <span class="type">string</span>(output)</span><br><span class="line">	parts := strings.Split(shellOut, <span class="string">&quot;\n&quot;</span>)</span><br><span class="line">	<span class="keyword">if</span> <span class="built_in">len</span>(parts) == <span class="number">0</span> &#123;</span><br><span class="line">		err = fmt.Errorf(<span class="string">&quot;containerPort=%s has no bindings&quot;</span>, containerPort)</span><br><span class="line">		<span class="keyword">return</span></span><br><span class="line">	&#125;</span><br><span class="line">	ipPort := parts[<span class="number">0</span>]</span><br><span class="line">	splits := strings.Split(ipPort, <span class="string">&quot;:&quot;</span>)</span><br><span class="line">	<span class="keyword">if</span> <span class="built_in">len</span>(splits) != <span class="number">2</span> &#123;</span><br><span class="line">		err = errors.New(<span class="string">&quot;invalid format&quot;</span>)</span><br><span class="line">		<span class="keyword">return</span></span><br><span class="line">	&#125;</span><br><span class="line">	port = splits[<span class="number">1</span>]</span><br><span class="line">	<span class="keyword">return</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h3 id="监控程序"><a href="#监控程序" class="headerlink" title="监控程序"></a>监控程序</h3><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">	<span class="string">&quot;bufio&quot;</span></span><br><span class="line">	<span class="string">&quot;context&quot;</span></span><br><span class="line">	<span class="string">&quot;flag&quot;</span></span><br><span class="line">	<span class="string">&quot;io&quot;</span></span><br><span class="line">	<span class="string">&quot;log&quot;</span></span><br><span class="line">	<span class="string">&quot;net&quot;</span></span><br><span class="line">	<span class="string">&quot;net/http&quot;</span></span><br><span class="line">	<span class="string">&quot;os&quot;</span></span><br><span class="line"></span><br><span class="line">	<span class="string">&quot;github.com/docker/docker/api/types&quot;</span></span><br><span class="line">	<span class="string">&quot;github.com/docker/docker/client&quot;</span></span><br><span class="line">	<span class="string">&quot;github.com/gorilla/websocket&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line">	logFileName <span class="type">string</span></span><br><span class="line">	containerID <span class="type">string</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line">	logOut   io.ReadCloser</span><br><span class="line">	upgrader = websocket.Upgrader&#123;</span><br><span class="line">		CheckOrigin: <span class="function"><span class="keyword">func</span><span class="params">(r *http.Request)</span></span> <span class="type">bool</span> &#123;</span><br><span class="line">			<span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line">		&#125;,</span><br><span class="line">	&#125; <span class="comment">// use default options</span></span><br><span class="line">	conn       *websocket.Conn</span><br><span class="line">	listenPort <span class="type">int</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">InitLog</span><span class="params">()</span></span> &#123;</span><br><span class="line">	logFile, err := os.OpenFile(logFileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, <span class="number">0644</span>)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		<span class="built_in">panic</span>(err)</span><br><span class="line">	&#125;</span><br><span class="line">	log.SetOutput(logFile)</span><br><span class="line">	log.SetPrefix(<span class="string">&quot;[sdu-weblab-deploy]&quot;</span>)</span><br><span class="line">	log.SetFlags(log.LstdFlags | log.Lshortfile | log.LUTC)</span><br><span class="line">	<span class="keyword">return</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">	<span class="comment">// fetch log stream and transfer it to websocket</span></span><br><span class="line">	flag.StringVar(&amp;logFileName, <span class="string">&quot;logFileName&quot;</span>, <span class="string">&quot;&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.StringVar(&amp;containerID, <span class="string">&quot;containerID&quot;</span>, <span class="string">&quot;&quot;</span>, <span class="string">&quot;&quot;</span>)</span><br><span class="line">	flag.Parse()</span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> <span class="built_in">len</span>(logFileName) == <span class="number">0</span> || <span class="built_in">len</span>(containerID) == <span class="number">0</span> &#123;</span><br><span class="line">		log.Fatal(<span class="string">&quot;key args missing&quot;</span>)</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// init log settings</span></span><br><span class="line">	InitLog()</span><br><span class="line"></span><br><span class="line">	log.Printf(<span class="string">&quot;Fetch log stream and transfer it to websocket...\n&quot;</span>)</span><br><span class="line">	ctx := context.Background()</span><br><span class="line">	<span class="keyword">var</span> err <span class="type">error</span></span><br><span class="line">	cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;New docker client fail, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	options := types.ContainerLogsOptions&#123;</span><br><span class="line">		ShowStdout: <span class="literal">true</span>,</span><br><span class="line">		ShowStderr: <span class="literal">true</span>,</span><br><span class="line">		Follow:     <span class="literal">true</span>,</span><br><span class="line">	&#125;</span><br><span class="line">	logOut, err = cli.ContainerLogs(ctx, containerID, options) <span class="comment">// remote</span></span><br><span class="line"></span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;Get log of container failed, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line"></span><br><span class="line">	listener, err := net.Listen(<span class="string">&quot;tcp&quot;</span>, <span class="string">&quot;:0&quot;</span>)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Fatalf(<span class="string">&quot;Net listen failed, err=%v&quot;</span>, err)</span><br><span class="line">	&#125;</span><br><span class="line">	listenPort = listener.Addr().(*net.TCPAddr).Port</span><br><span class="line">	<span class="comment">// todo register to center</span></span><br><span class="line">	log.Printf(<span class="string">&quot;Websocket port is %d&quot;</span>, listenPort)</span><br><span class="line"></span><br><span class="line">	http.HandleFunc(<span class="string">&quot;/logs&quot;</span>, socketHandler)</span><br><span class="line">	log.Fatal(http.Serve(listener, <span class="literal">nil</span>))</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">socketHandler</span><span class="params">(w http.ResponseWriter, r *http.Request)</span></span> &#123;</span><br><span class="line">	<span class="comment">// Upgrade our raw HTTP connection to a websocket based one</span></span><br><span class="line">	_conn, err := upgrader.Upgrade(w, r, <span class="literal">nil</span>)</span><br><span class="line">	<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">		log.Print(<span class="string">&quot;Error during connection upgradation:&quot;</span>, err)</span><br><span class="line">		os.Exit(<span class="number">3</span>)</span><br><span class="line">	&#125;</span><br><span class="line">	conn = _conn</span><br><span class="line">	<span class="keyword">defer</span> conn.Close()</span><br><span class="line"></span><br><span class="line">	reader := bufio.NewReader(logOut)</span><br><span class="line">	<span class="comment">// The event loop</span></span><br><span class="line">	<span class="keyword">for</span> &#123;</span><br><span class="line">		message, err := reader.ReadString(<span class="string">&#x27;\n&#x27;</span>)</span><br><span class="line"></span><br><span class="line">		<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">			<span class="keyword">if</span> err == io.EOF &#123;</span><br><span class="line">				exitAndNotifyPeer(message, <span class="number">0</span>)</span><br><span class="line">			&#125;</span><br><span class="line">			exitAndNotifyPeer(<span class="string">&quot;\n&quot;</span>, <span class="number">1</span>)</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="comment">//log.Printf(&quot;Log read is: %s&quot;, message)</span></span><br><span class="line">		err = conn.WriteMessage(websocket.TextMessage, []<span class="type">byte</span>(message))</span><br><span class="line">		<span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">			log.Println(<span class="string">&quot;Error during message writing: &quot;</span>, err)</span><br><span class="line">			os.Exit(<span class="number">2</span>)</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">exitAndNotifyPeer</span><span class="params">(msg <span class="type">string</span>, exitCode <span class="type">int</span>)</span></span> &#123;</span><br><span class="line">	conn.WriteMessage(websocket.CloseMessage, []<span class="type">byte</span>(msg))</span><br><span class="line">	os.Exit(exitCode)</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>





<h3 id="构建脚本"><a href="#构建脚本" class="headerlink" title="构建脚本"></a>构建脚本</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">jsy@wzai:~/devops/docker_workdir$ tree</span><br><span class="line">.</span><br><span class="line">├── deploy</span><br><span class="line">│   ├── go.mod</span><br><span class="line">│   ├── go.sum</span><br><span class="line">│   ├── main.go</span><br><span class="line">│   └── output</span><br><span class="line">│       └── bin</span><br><span class="line">│           └── deploy</span><br><span class="line">├── monitor</span><br><span class="line">│   ├── go.mod</span><br><span class="line">│   ├── go.sum</span><br><span class="line">│   ├── main.go</span><br><span class="line">│   └── output</span><br><span class="line">│       └── bin</span><br><span class="line">│           └── monitor</span><br></pre></td></tr></table></figure>

<p><code>go build</code></p>
<p>首次构建</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">cd ~/devops/docker_workdir/</span><br><span class="line">cd deploy &amp;&amp; go mod tidy &amp;&amp; mkdir -p output/bin &amp;&amp; go build -o output/bin/deploy</span><br><span class="line">cd monitor &amp;&amp; go mod tidy &amp;&amp; mkdir -p output/bin &amp;&amp; go build -o output/bin/monitor</span><br></pre></td></tr></table></figure>

<p>后面更新</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">cd ~/devops/docker_workdir/</span><br><span class="line">cd deploy &amp;&amp; go mod tidy &amp;&amp; go build -o output/bin/deploy</span><br><span class="line">cd ~/devops/docker_workdir/</span><br><span class="line">cd monitor &amp;&amp; go mod tidy &amp;&amp; go build -o output/bin/monitor</span><br></pre></td></tr></table></figure>



<p>构建或更新软链接</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo ln -f -s /home/jsy/devops/docker_workdir/deploy/output/bin/deploy /opt/sduweblab/bin/deploy</span><br><span class="line">sudo ln -f -s /home/jsy/devops/docker_workdir/monitor/output/bin/monitor /opt/sduweblab/bin/monitor</span><br></pre></td></tr></table></figure>





<p><del>更新产物</del></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">cd ~/devops/docker_workdir/</span><br><span class="line">cd deploy &amp;&amp; go mod tidy &amp;&amp; go build -o output/bin/deploy</span><br><span class="line">cd ~/devops/docker_workdir/</span><br><span class="line">cd monitor &amp;&amp; go mod tidy &amp;&amp; go build -o output/bin/monitor</span><br><span class="line">sudo ln -f -s /home/jsy/devops/docker_workdir/deploy/output/bin/deploy /opt/sduweblab/bin/deploy</span><br><span class="line">sudo ln -f -s /home/jsy/devops/docker_workdir/monitor/output/bin/monitor /opt/sduweblab/bin/monitor</span><br></pre></td></tr></table></figure>


<p>更新产物（推送镜像到harbor也使用go改写）</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">sudo rm /opt/sduweblab/bin/deploy</span><br><span class="line">sudo rm /opt/sduweblab/bin/monitor</span><br><span class="line">sudo rm /opt/sduweblab/bin/push_to_harbor</span><br><span class="line">cd ~/devops/docker_workdir/</span><br><span class="line">cd deploy &amp;&amp; go mod tidy &amp;&amp; go build -o output/bin/deploy</span><br><span class="line">cd ~/devops/docker_workdir/</span><br><span class="line">cd monitor &amp;&amp; go mod tidy &amp;&amp; go build -o output/bin/monitor</span><br><span class="line">cd ~/devops/docker_workdir/</span><br><span class="line">cd push_to_harbor &amp;&amp; go mod tidy &amp;&amp; go build -o output/bin/push_to_harbor</span><br><span class="line">sudo cp /home/jsy/devops/docker_workdir/deploy/output/bin/deploy /opt/sduweblab/bin/deploy</span><br><span class="line">sudo cp /home/jsy/devops/docker_workdir/monitor/output/bin/monitor /opt/sduweblab/bin/monitor</span><br><span class="line">sudo cp /home/jsy/devops/docker_workdir/push_to_harbor/output/bin/push_to_harbor /opt/sduweblab/bin/push_to_harbor</span><br><span class="line">cd</span><br></pre></td></tr></table></figure>





<p>原先的shell换成go程序即可。</p>
<p>测试脚本：</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cd deploy &amp;&amp; /opt/sduweblab/bin/deploy --harbor_url=<span class="number">211.87</span><span class="number">.224</span><span class="number">.233</span>:<span class="number">8930</span> --harbor_repo=sdu-weblab --project=springboot-helloworld --version=origin/master --container_port=<span class="number">8080</span></span><br></pre></td></tr></table></figure>


      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2022/05/06/Sdu-Devops-01/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2022/05/06/Sdu-Devops-01/" class="post-title-link" itemprop="url">安装Docker、Harbor、Jenkins、Gitlab</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2022-05-06 21:00:00" itemprop="dateCreated datePublished" datetime="2022-05-06T21:00:00+08:00">2022-05-06</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-04-05 01:48:17" itemprop="dateModified" datetime="2023-04-05T01:48:17+08:00">2023-04-05</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/DevOps/" itemprop="url" rel="index"><span itemprop="name">DevOps</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="系统平台信息"><a href="#系统平台信息" class="headerlink" title="系统平台信息"></a>系统平台信息</h1><p>OS:  Linux Mint 20.3 Una</p>
<p>Kernel Release: 5.13.0-35-generic</p>
<p>Hardware Platform: x86_64</p>
<p>CPU Model Name:  Intel(R) Xeon(R) CPU E5-2680 v3 @ 2.50GHz</p>
<p>CPU Cores: 48</p>
<p>Main Memory: 125GB</p>
<p>IP: 211.87.224.233</p>
<h1 id="依赖"><a href="#依赖" class="headerlink" title="依赖"></a>依赖</h1><h2 id="Docker"><a href="#Docker" class="headerlink" title="Docker"></a>Docker</h2><p>version 20.10.14</p>
<p>旧版本的 Docker </p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt-get remove docker docker-engine docker.io containerd runc</span><br></pre></td></tr></table></figure>



<h4 id="设置存储库"><a href="#设置存储库" class="headerlink" title="设置存储库"></a>设置存储库</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">sudo apt-get update</span><br><span class="line"></span><br><span class="line">sudo apt-get install \</span><br><span class="line">    ca-certificates \</span><br><span class="line">    curl \</span><br><span class="line">    gnupg \</span><br><span class="line">    lsb-release</span><br></pre></td></tr></table></figure>



<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt-get update</span><br></pre></td></tr></table></figure>



<p>这里没有安装 docker-compose</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt-get install docker-ce docker-ce-cli containerd.io</span><br></pre></td></tr></table></figure>



<p>apt-cache madison docker-ce</p>
<p>version &#x3D; <code>5:20.10.16~3-0~ubuntu-focal</code></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt-get install docker-ce=5:20.10.16~3-0~ubuntu-focal docker-ce-cli=5:20.10.16~3-0~ubuntu-focal containerd.io</span><br></pre></td></tr></table></figure>





<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">#1.配置阿里云的gpg</span><br><span class="line">curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -</span><br><span class="line"></span><br><span class="line">#2.配置阿里云的docker镜像</span><br><span class="line">sudo add-apt-repository &quot;deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable&quot;</span><br><span class="line"></span><br><span class="line">#3.执行安装命令</span><br><span class="line">sudo apt-get install docker-ce docker-ce-cli containerd.io</span><br></pre></td></tr></table></figure>






<h3 id="Docker-Compose"><a href="#Docker-Compose" class="headerlink" title="Docker-Compose"></a>Docker-Compose</h3><p>docker-compose version 1.29.2</p>
<p>安装</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo curl -L &quot;https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)&quot; -o /usr/bin/docker-compose</span><br></pre></td></tr></table></figure>

<p>加上可执行权限</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo chmod +x /usr/bin/docker-compose</span><br></pre></td></tr></table></figure>



<h2 id="Go"><a href="#Go" class="headerlink" title="Go"></a>Go</h2><p>go version go1.17.7 linux&#x2F;amd6</p>
<h2 id="Java"><a href="#Java" class="headerlink" title="Java"></a>Java</h2><p>openjdk version “1.8.0_312”</p>
<p>path: &#x2F;usr&#x2F;lib&#x2F;jvm&#x2F;java-8-openjdk-amd64</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo update-alternatives --config java</span><br></pre></td></tr></table></figure>

<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">#</span><span class="language-bash">output</span></span><br><span class="line">There are 3 choices for the alternative java (providing /usr/bin/java).</span><br><span class="line"></span><br><span class="line">  Selection    Path                                            Priority   Status</span><br><span class="line">------------------------------------------------------------</span><br><span class="line">* 0            /usr/lib/jvm/java-17-openjdk-amd64/bin/java      1711      auto mode</span><br><span class="line">  1            /usr/lib/jvm/java-11-openjdk-amd64/bin/java      1111      manual mode</span><br><span class="line">  2            /usr/lib/jvm/java-17-openjdk-amd64/bin/java      1711      manual mode</span><br><span class="line">  3            /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java   1081      manual mode</span><br><span class="line"></span><br><span class="line">Press &lt;enter&gt; to keep the current choice[*], or type selection number:</span><br></pre></td></tr></table></figure>

<p><code>java -version</code> 检查</p>
<h2 id="Maven"><a href="#Maven" class="headerlink" title="Maven"></a>Maven</h2><p>Apache Maven 3.8.5</p>
<p>path: &#x2F;opt&#x2F;apache-maven-3.8.5</p>
<ul>
<li>jdk8编译插件配置</li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">profile</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">id</span>&gt;</span>jdk-8<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">activation</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">activeByDefault</span>&gt;</span>true<span class="tag">&lt;/<span class="name">activeByDefault</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">jdk</span>&gt;</span>8<span class="tag">&lt;/<span class="name">jdk</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">activation</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">maven.compiler.source</span>&gt;</span>8<span class="tag">&lt;/<span class="name">maven.compiler.source</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">maven.compiler.target</span>&gt;</span>8<span class="tag">&lt;/<span class="name">maven.compiler.target</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">maven.compiler.compilerVersion</span>&gt;</span>8<span class="tag">&lt;/<span class="name">maven.compiler.compilerVersion</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">profile</span>&gt;</span></span><br></pre></td></tr></table></figure>



<ul>
<li>maven仓库源</li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">mirror</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">id</span>&gt;</span>alimaven<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">mirrorOf</span>&gt;</span>central<span class="tag">&lt;/<span class="name">mirrorOf</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">name</span>&gt;</span>Aliyun maven<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">url</span>&gt;</span>https://maven.aliyun.com/repository/central<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mirror</span>&gt;</span></span><br></pre></td></tr></table></figure>





<h2 id="Gitlab"><a href="#Gitlab" class="headerlink" title="Gitlab"></a>Gitlab</h2><p>使用docker安装 Gitlab</p>
<h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker search gitlab/</span><br></pre></td></tr></table></figure>


<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker pull gitlab/gitlab-ce:latest</span><br></pre></td></tr></table></figure>

<h3 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo mkdir -p /srv/gitlab &amp;&amp; echo &quot;export GITLAB_HOME=/srv/gitlab&quot; &gt;&gt; .profile &amp;&amp; source .profile</span><br></pre></td></tr></table></figure>



<p>docker-compose.yml</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">version:</span> <span class="string">&#x27;3.6&#x27;</span></span><br><span class="line"><span class="attr">services:</span> </span><br><span class="line">  <span class="attr">web:</span> </span><br><span class="line">    <span class="attr">image:</span> <span class="string">&#x27;gitlab/gitlab-ce:latest&#x27;</span></span><br><span class="line">    <span class="attr">container_name:</span> <span class="string">sdu-weblab-gitlab</span></span><br><span class="line">    <span class="attr">restart:</span> <span class="string">always</span></span><br><span class="line">    <span class="attr">hostname:</span> <span class="string">&#x27;211.87.224.233&#x27;</span></span><br><span class="line">    <span class="attr">environment:</span> </span><br><span class="line">      <span class="attr">GITLAB_OMNIBUS_CONFIG:</span> <span class="string">|</span></span><br><span class="line"><span class="string">        external_url &#x27;http://211.87.224.233:8929&#x27;</span></span><br><span class="line"><span class="string">        gitlab_rails[&#x27;gitlab_shell_ssh_port&#x27;] = 2224</span></span><br><span class="line"><span class="string"></span>    <span class="attr">ports:</span> </span><br><span class="line">      <span class="bullet">-</span> <span class="string">&#x27;8929:8929&#x27;</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&#x27;2224:22&#x27;</span></span><br><span class="line">    <span class="attr">volumes:</span> </span><br><span class="line">      <span class="bullet">-</span> <span class="string">&#x27;$GITLAB_HOME/config:/etc/gitlab&#x27;</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&#x27;$GITLAB_HOME/logs:/var/log/gitlab&#x27;</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&#x27;$GITLAB_HOME/data:/var/opt/gitlab&#x27;</span></span><br></pre></td></tr></table></figure>

<p>注意点:</p>
<p>external_url中的端口需要和ports映射的容器端口保持一致;</p>
<p>gitlab_rails[‘gitlab_shell_ssh_port’]要和宿主机端口保持一致。</p>
<h3 id="启动"><a href="#启动" class="headerlink" title="启动"></a>启动</h3><p>构建并启动</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker-compose -f ./docker-compose.yml up -d # 如果文件名为`docker-compose.yml`可以不指定-f</span><br></pre></td></tr></table></figure>

<p>强制重启</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker-compose up -d --force-recreate</span><br></pre></td></tr></table></figure>



<p>重新构建并启动（反映yml的变化，一定是最新的）</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker-compose down &amp;&amp; docker-compose up -d</span><br></pre></td></tr></table></figure>





<p>注意：这个服务启动时间比较长，须等待一两分钟。</p>
<p>检查是否成功运行：浏览器访问 <a target="_blank" rel="noopener" href="http://211.87.224.233:8929/">http://211.87.224.233:8929</a>  (这个8989是映射的宿主机port，当然你可以映射成其他端口)</p>
<p>如果访问失败，注意云服务器的安全组或者firewall的端口开放情况（sudo ufw status）</p>
<h3 id="使用Gitlab"><a href="#使用Gitlab" class="headerlink" title="使用Gitlab"></a>使用Gitlab</h3><p>默认管理员账户：root</p>
<p>查看配置文件得到密码</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">注意我们已经配置好数据卷映射了</span></span><br><span class="line">sudo cat $GITLAB_HOME/config/initial_root_password</span><br></pre></td></tr></table></figure>

<p>可以再去修改一下密码（设置为： sduweblab）</p>
<blockquote>
<p><a target="_blank" rel="noopener" href="https://docs.gitlab.com/ee/install/docker.html">https://docs.gitlab.com/ee/install/docker.html</a></p>
<p><a target="_blank" rel="noopener" href="https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template">https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template</a></p>
</blockquote>
<h4 id="New-project-gt-Create-blank-project"><a href="#New-project-gt-Create-blank-project" class="headerlink" title="[New project]-&gt;[Create blank project]"></a>[New project]-&gt;[Create blank project]</h4><h4 id="Mac配置ssh登录"><a href="#Mac配置ssh登录" class="headerlink" title="Mac配置ssh登录"></a>Mac配置ssh登录</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">jisongyang@SongyangJi-MacBookAir .ssh % ssh -p 2224 -T git@211.87.224.233</span><br><span class="line">ssh: connect to host 211.87.224.233 port 2224: Connection refused</span><br></pre></td></tr></table></figure>



<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">jisongyang@SongyangJi-MacBookAir .ssh % ssh -T git@211.87.224.233</span><br><span class="line">git@211.87.224.233&#x27;s password:</span><br></pre></td></tr></table></figure>










<h2 id="Jenkins"><a href="#Jenkins" class="headerlink" title="Jenkins"></a>Jenkins</h2><h3 id="安装-1"><a href="#安装-1" class="headerlink" title="安装"></a>安装</h3><p>jenkins: 2.346</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker pull jenkins/jenkins:latest</span><br></pre></td></tr></table></figure>

<h3 id="配置-1"><a href="#配置-1" class="headerlink" title="配置"></a>配置</h3><p>docker-compse yml</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">version:</span> <span class="string">&#x27;3.6&#x27;</span></span><br><span class="line"><span class="attr">services:</span> </span><br><span class="line">  <span class="attr">jenkins:</span> </span><br><span class="line">    <span class="attr">image:</span> <span class="string">jenkins/jenkins:latest</span></span><br><span class="line">    <span class="attr">container_name:</span> <span class="string">sdu-weblab-jenkins-latest</span></span><br><span class="line">    <span class="attr">restart:</span> <span class="string">always</span></span><br><span class="line">    <span class="attr">ports:</span> </span><br><span class="line">      <span class="bullet">-</span> <span class="string">&quot;18080:8080&quot;</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&quot;50000:50000&quot;</span></span><br><span class="line">    <span class="attr">volumes:</span> </span><br><span class="line">      <span class="bullet">-</span> <span class="string">./data:/var/jenkins_home</span> <span class="comment"># jenkins:latest 的home目录，包括插件和配置等等</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&#x27;$MAVEN_HOME:/opt/maven&#x27;</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&#x27;$JAVA_HOME:/opt/java8&#x27;</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">/var/run/docker.sock:/var/run/docker.sock</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">/usr/bin/docker:/usr/bin/docker</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">/etc/docker/daemon.json:/etc/docker/daemon.json</span></span><br></pre></td></tr></table></figure>

<p>由于jenkins承担了打包的任务，因此将宿主机的jdk和maven挂载上去（另一种做法是自定义一个包含jdk和maven的jenkins镜像，有待尝试）。</p>
<p>(其中有关docker的配置后面再解释)</p>
<h3 id="修改镜像源"><a href="#修改镜像源" class="headerlink" title="修改镜像源"></a>修改镜像源</h3><p>否则大概率下载插件会失败！</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vim data/hudson.model.UpdateCenter.xml</span><br></pre></td></tr></table></figure>



<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=&#x27;1.1&#x27; encoding=&#x27;UTF-8&#x27;?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">sites</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">site</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">id</span>&gt;</span>default<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">url</span>&gt;</span>https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">site</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">sites</span>&gt;</span></span><br></pre></td></tr></table></figure>



<h3 id="启动-1"><a href="#启动-1" class="headerlink" title="启动"></a>启动</h3><p>启动之后，查看日志发现写数据卷的时候权限不够。（有可能）</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo chmod -R 777 data/</span><br></pre></td></tr></table></figure>

<p>给足data文件夹权限。(奇怪的是，docker-compose restart的时候还是要发现写权限不够)</p>
<p>检查是否启动成功，访问 <a target="_blank" rel="noopener" href="http://211.87.224.233:18080/">http://211.87.224.233:18080/</a></p>
<p><strong>进入容器</strong></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker exec -it sdu-weblab-jenkins bash</span><br></pre></td></tr></table></figure>





<h3 id="登录Jenkins"><a href="#登录Jenkins" class="headerlink" title="登录Jenkins"></a>登录Jenkins</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">查看管理员日志密码（查看文件 or 使用docker-compose logs -f 查看日志）</span></span><br><span class="line">cat data/secrets/initialAdminPassword # 可能没有文件读权限</span><br></pre></td></tr></table></figure>



<p>admin: f991e76fb99b4e3993ccdb52a9eaa8f7</p>
<h3 id="管理插件"><a href="#管理插件" class="headerlink" title="管理插件"></a>管理插件</h3><p>入口：[Manage Jenkins]-&gt;[System Configuration]-&gt;[Manage Plugins]</p>
<p><strong>必备插件</strong></p>
<ul>
<li>Git Parameter</li>
<li>Publish Over SSH</li>
<li>Gitlab</li>
<li>Pipeline</li>
</ul>
<h3 id="全局配置"><a href="#全局配置" class="headerlink" title="全局配置"></a>全局配置</h3><h4 id="Manage-Jenkins-gt-Global-Tool-Configuration"><a href="#Manage-Jenkins-gt-Global-Tool-Configuration" class="headerlink" title="[Manage Jenkins]-&gt;[Global Tool Configuration]"></a>[Manage Jenkins]-&gt;[Global Tool Configuration]</h4><p>配置jdk和maven的home。<br>其中MAVEN_HOME配置为<code>/opt/maven</code>; JAVA_HOME配置为<code>/opt/java8</code>（不错这两个文件夹其实是从宿主机映射过来的，其真实路径分别为宿主机的<code>$MAVEN_HOME</code>、<code>JAVA_HOME</code>，也就是<code>/opt/apache-maven-3.8.5</code>、<code>/usr/lib/jvm/java-8-openjdk-amd64</code>）</p>
<h4 id="Manage-Jenkins-gt-Configure-System-gt-Publish-over-SSH"><a href="#Manage-Jenkins-gt-Configure-System-gt-Publish-over-SSH" class="headerlink" title="[Manage Jenkins]-&gt;[Configure System]-&gt;[Publish over SSH]"></a>[Manage Jenkins]-&gt;[Configure System]-&gt;[Publish over SSH]</h4><p>配置远程发布的机器，这里为了方便就配置本机即可。（这里在配置auth校验的时候最好使用密码校验，配置的私钥话可能因为不合规导致失败）</p>
<h3 id="在Jenkins容器内使用docker"><a href="#在Jenkins容器内使用docker" class="headerlink" title="在Jenkins容器内使用docker"></a>在Jenkins容器内使用docker</h3><p>两种方案，一种是在Jenkins镜像里就有docker，另一种是使用宿主机的docker。</p>
<p>这里我们采取后者。</p>
<blockquote>
<p>参考 <a target="_blank" rel="noopener" href="https://www.dockone.io/article/431">https://www.dockone.io/article/431</a></p>
</blockquote>
<p>这里只讲操作不讲原因和原理。</p>
<p>第一步修改权限：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cd /var/run</span><br><span class="line">sudo chown root:root docker.sock &amp;&amp; sudo chmod o+rw docker.sock</span><br></pre></td></tr></table></figure>



<p>第二步修改jenkins的docker-compose.yml</p>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">version:</span> <span class="string">&#x27;3.6&#x27;</span></span><br><span class="line"><span class="attr">services:</span> </span><br><span class="line">  <span class="attr">jenkins:</span> </span><br><span class="line">    <span class="attr">image:</span> <span class="string">jenkins/jenkins:latest</span></span><br><span class="line">    <span class="attr">container_name:</span> <span class="string">sdu-weblab-jenkins-latest</span></span><br><span class="line">    <span class="attr">restart:</span> <span class="string">always</span></span><br><span class="line">    <span class="attr">ports:</span> </span><br><span class="line">      <span class="bullet">-</span> <span class="string">&quot;18080:8080&quot;</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&quot;50000:50000&quot;</span></span><br><span class="line">    <span class="attr">volumes:</span> </span><br><span class="line">      <span class="bullet">-</span> <span class="string">./data:/var/jenkins_home</span> <span class="comment"># jenkins:latest 的home目录，包括插件和配置等等</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&#x27;$MAVEN_HOME:/opt/maven&#x27;</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">&#x27;$JAVA_HOME:/opt/java8&#x27;</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">/var/run/docker.sock:/var/run/docker.sock</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">/usr/bin/docker:/usr/bin/docker</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">/etc/docker/daemon.json:/etc/docker/daemon.json</span></span><br></pre></td></tr></table></figure>

<p>重点就是挂载宿主机三个文件即可，至于这里为什么mount这三个就足够了，不做解释。</p>
<p>记住重启一下</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker-compose up -d</span><br></pre></td></tr></table></figure>



<p>检查：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">jsy@wzai:~$ docker exec -it a6d580bfb4e1 bash</span><br><span class="line">jenkins@a6d580bfb4e1:/$</span><br><span class="line">jenkins@a6d580bfb4e1:/$</span><br><span class="line">jenkins@a6d580bfb4e1:/$ docker version</span><br></pre></td></tr></table></figure>


<h2 id="Harbor"><a href="#Harbor" class="headerlink" title="Harbor"></a>Harbor</h2><h3 id="下载"><a href="#下载" class="headerlink" title="下载"></a>下载</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">wget https://github.com/goharbor/harbor/releases/download/v2.5.0/harbor-offline-installer-v2.5.0.tgz</span><br></pre></td></tr></table></figure>



<h3 id="安装-2"><a href="#安装-2" class="headerlink" title="安装"></a>安装</h3><p><strong>解压</strong></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tar -xzvf harbor-offline-installer-v2.5.0.tgz -C ./</span><br></pre></td></tr></table></figure>

<p>解压结果</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">jsy@wzai:~/devops$ ls harbor/</span><br><span class="line">common.sh  harbor.v2.5.0.tar.gz  harbor.yml.tmpl  install.sh  LICENSE  prepare</span><br></pre></td></tr></table></figure>



<p><strong>编辑配置文件</strong></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cp harbor.yml.tmpl harbor.yml</span><br></pre></td></tr></table></figure>

<p>修改hostname为主机ip，将htts相关内容注释掉（暂时不需要使用到https）、修改admin密码、修改http端口号。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">hostname: 211.87.224.233</span><br><span class="line"></span><br><span class="line">http:</span><br><span class="line">  # port for http, default is 80. If https enabled, this port will redirect to https port</span><br><span class="line">  port: 8930</span><br><span class="line">  </span><br><span class="line">harbor_admin_password: admin</span><br></pre></td></tr></table></figure>




<p>安装：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./prepare &amp;&amp; sudo ./install.sh</span><br></pre></td></tr></table></figure>



<p>install过程分为这么几步：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[Step 0]: checking if docker is installed ...</span><br><span class="line">[Step 1]: checking docker-compose is installed ...</span><br><span class="line">[Step 2]: loading Harbor images ...</span><br><span class="line">[Step 3]: preparing environment ...</span><br><span class="line">[Step 4]: preparing harbor configs ...</span><br></pre></td></tr></table></figure>



<p>安装完毕后检查一下：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">jsy@wzai:~/devops/harbor$ sudo docker-compose ps</span><br><span class="line">      Name                    Command                 State                  Ports</span><br><span class="line">----------------------------------------------------------------------------------------------</span><br><span class="line">harbor-core         /harbor/entrypoint.sh          Up (healthy)</span><br><span class="line">harbor-db           /docker-entrypoint.sh 96 13    Up (healthy)</span><br><span class="line">harbor-jobservice   /harbor/entrypoint.sh          Up (healthy)</span><br><span class="line">harbor-log          /bin/sh -c /usr/local/bin/     Up (healthy)   127.0.0.1:1514-&gt;10514/tcp</span><br><span class="line">                    ...</span><br><span class="line">harbor-portal       nginx -g daemon off;           Up (healthy)</span><br><span class="line">nginx               nginx -g daemon off;           Up (healthy)   0.0.0.0:8930-&gt;8080/tcp,:::89</span><br><span class="line">                                                                  30-&gt;8080/tcp</span><br><span class="line">redis               redis-server /etc/redis.conf   Up (healthy)</span><br><span class="line">registry            /home/harbor/entrypoint.sh     Up (healthy)</span><br><span class="line">registryctl         /home/harbor/start.sh          Up (healthy)</span><br></pre></td></tr></table></figure>



<p>浏览器访问检查</p>
<p>localhost:8930</p>
<h3 id="拉取-x2F-推送镜像"><a href="#拉取-x2F-推送镜像" class="headerlink" title="拉取&#x2F;推送镜像"></a>拉取&#x2F;推送镜像</h3><p>下一步使用docker login到私服，需要将私服的htp地址配置到docker的不安全register中；</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo vim /etc/docker/daemon.json</span><br></pre></td></tr></table></figure>

<p>写入</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;insecure-registries&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="string">&quot;211.87.224.233:8930&quot;</span><span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure>

<p>必须重启docker服务</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo systemctl restart docker</span><br></pre></td></tr></table></figure>



<p>镜像名称要求：私服地址&#x2F;项目名&#x2F;镜像名:版本</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]</span></span><br><span class="line">docker tag IMAGE_ID 私服地址/项目名/镜像名:版本</span><br></pre></td></tr></table></figure>



<p><strong>举例</strong></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker tag hello-world $HARBOR_URL/sdu-weblab/hello-world # 配置一下HARBOR_URL环境变量</span><br></pre></td></tr></table></figure>

<p>检查一下</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">&gt; </span><span class="language-bash">docker images</span> </span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">211.87.224.233:8930/sdu-weblab/hello-world   latest             feb5d9fea6a5   7 months ago   13.3kB</span></span><br></pre></td></tr></table></figure>



<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">先登录后推送</span></span><br><span class="line">docker login -u admin -p admin $HARBOR_URL</span><br><span class="line">docker push 211.87.224.233:8930/sdu-weblab/hello-world</span><br></pre></td></tr></table></figure>



<p>推送成功</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">jsy@wzai:~$ docker push 211.87.224.233:8930/sdu-weblab/hello-world</span><br><span class="line">Using default tag: latest</span><br><span class="line">The push refers to repository [211.87.224.233:8930/sdu-weblab/hello-world]</span><br><span class="line">e07ee1baac5f: Pushed</span><br><span class="line">latest: digest: sha256:f54a58bc1aac5ea1a25d796ae155dc228b3f0e11d046ae276b39c4bf2f13d8c4 size: 525</span><br></pre></td></tr></table></figure>



<p>拉取镜像也是一样的。</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">先删除本地的</span></span><br><span class="line">docker image rm 211.87.224.233:8930/sdu-weblab/hello-world</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">直接粘贴浏览器的命令</span></span><br><span class="line">docker pull 211.87.224.233:8930/sdu-weblab/hello-world@sha256:f54a58bc1aac5ea1a25d796ae155dc228b3f0e11d046ae276b39c4bf2f13d8c4</span><br></pre></td></tr></table></figure>

<blockquote>
<p><a target="_blank" rel="noopener" href="https://github.com/jenkinsci/docker/blob/master/README.md">https://github.com/jenkinsci/docker/blob/master/README.md</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2022/05/02/linux%E6%9F%A5%E7%9C%8B%E7%A1%AC%E4%BB%B6%E3%80%81%E7%B3%BB%E7%BB%9F%E4%BF%A1%E6%81%AF/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2022/05/02/linux%E6%9F%A5%E7%9C%8B%E7%A1%AC%E4%BB%B6%E3%80%81%E7%B3%BB%E7%BB%9F%E4%BF%A1%E6%81%AF/" class="post-title-link" itemprop="url">Linux查看系统信息——内存、CPU、磁盘</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2022-05-02 21:00:00" itemprop="dateCreated datePublished" datetime="2022-05-02T21:00:00+08:00">2022-05-02</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-04-08 16:08:57" itemprop="dateModified" datetime="2023-04-08T16:08:57+08:00">2023-04-08</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Linux/" itemprop="url" rel="index"><span itemprop="name">Linux</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="1-查看操作系统"><a href="#1-查看操作系统" class="headerlink" title="1. 查看操作系统"></a>1. 查看操作系统</h1><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cat /etc/issue</span><br></pre></td></tr></table></figure>

<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">lsb_release -a</span><br></pre></td></tr></table></figure>



<h1 id="2-查看内存信息"><a href="#2-查看内存信息" class="headerlink" title="2. 查看内存信息"></a>2. 查看内存信息</h1><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cat /proc/meminfo</span><br></pre></td></tr></table></figure>

<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">output</span></span><br><span class="line">MemTotal:        4009020 kB</span><br><span class="line">MemFree:          135628 kB</span><br><span class="line">MemAvailable:    1925688 kB</span><br><span class="line">Buffers:          282728 kB</span><br><span class="line">Cached:          1324092 kB</span><br><span class="line">SwapCached:         1612 kB</span><br><span class="line">Active:           807888 kB</span><br><span class="line">Inactive:        2533284 kB</span><br><span class="line">Active(anon):      43472 kB</span><br><span class="line">Inactive(anon):  1709556 kB</span><br><span class="line">Active(file):     764416 kB</span><br><span class="line">Inactive(file):   823728 kB</span><br><span class="line">Unevictable:        6976 kB</span><br><span class="line">Mlocked:               0 kB</span><br><span class="line">SwapTotal:       2097148 kB</span><br><span class="line">SwapFree:        2052092 kB</span><br><span class="line">Dirty:             16828 kB</span><br><span class="line">Writeback:             0 kB</span><br><span class="line">AnonPages:       1739820 kB</span><br><span class="line">Mapped:           482588 kB</span><br><span class="line">Shmem:             26376 kB</span><br><span class="line">KReclaimable:     385472 kB</span><br><span class="line">Slab:             458420 kB</span><br><span class="line">SReclaimable:     385472 kB</span><br><span class="line">SUnreclaim:        72948 kB</span><br><span class="line">KernelStack:       12944 kB</span><br><span class="line">PageTables:        22444 kB</span><br><span class="line">NFS_Unstable:          0 kB</span><br><span class="line">Bounce:                0 kB</span><br><span class="line">WritebackTmp:          0 kB</span><br><span class="line">CommitLimit:     4101656 kB</span><br><span class="line">Committed_AS:    6306356 kB</span><br><span class="line">VmallocTotal:   133143461888 kB</span><br><span class="line">VmallocUsed:       27320 kB</span><br><span class="line">VmallocChunk:          0 kB</span><br><span class="line">Percpu:             1360 kB</span><br><span class="line">HardwareCorrupted:     0 kB</span><br><span class="line">AnonHugePages:         0 kB</span><br><span class="line">ShmemHugePages:        0 kB</span><br><span class="line">ShmemPmdMapped:        0 kB</span><br><span class="line">FileHugePages:         0 kB</span><br><span class="line">FilePmdMapped:         0 kB</span><br><span class="line">CmaTotal:          32768 kB</span><br><span class="line">CmaFree:           30140 kB</span><br><span class="line">HugePages_Total:       0</span><br><span class="line">HugePages_Free:        0</span><br><span class="line">HugePages_Rsvd:        0</span><br><span class="line">HugePages_Surp:        0</span><br><span class="line">Hugepagesize:       2048 kB</span><br><span class="line">Hugetlb:               0 kB</span><br></pre></td></tr></table></figure>



<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">默认</span></span><br><span class="line">free</span><br></pre></td></tr></table></figure>



<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">              总计         已用        空闲      共享    缓冲/缓存    可用</span><br><span class="line">内存：     4009020     1889432      115696       26380     2003892     1917212</span><br><span class="line">交换：     2097148       46848     2050300</span><br></pre></td></tr></table></figure>



<h1 id="3-查看CPU信息"><a href="#3-查看CPU信息" class="headerlink" title="3. 查看CPU信息"></a>3. 查看CPU信息</h1><ol>
<li>查看物理CPU个数</li>
</ol>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cat /proc/cpuinfo | grep &quot;physical id&quot; | sort | uniq | wc -l</span><br></pre></td></tr></table></figure>

<ol start="2">
<li>查看每个物理CPU中core的个数(即核数)</li>
</ol>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cat /proc/cpuinfo | grep &quot;cpu cores&quot; | uniq</span><br></pre></td></tr></table></figure>

<ol start="3">
<li>查看逻辑CPU的个数</li>
</ol>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cat /proc/cpuinfo | grep &quot;processor&quot; | wc -l</span><br></pre></td></tr></table></figure>

<ol start="4">
<li>查看CPU信息（型号）</li>
</ol>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c</span><br></pre></td></tr></table></figure>

<ol start="5">
<li>查看是否开启超线程技术（只有intel的处理器支持）<br>如果启用此技术那么，每个物理核心又可分为两个逻辑处理器。<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cat /proc/cpuinfo | grep -e &quot;cpu cores&quot;  -e &quot;siblings&quot; | sort | uniq</span><br></pre></td></tr></table></figure>
如果cpu cores数量和siblings数量一致，则没有启用超线程，否则超线程被启用。</li>
</ol>
<h1 id="4-查看CPU的负载"><a href="#4-查看CPU的负载" class="headerlink" title="4. 查看CPU的负载"></a>4. 查看CPU的负载</h1><p>平均负载是指上一分钟同时处于就绪状态的平均进程数。</p>
<p>如果CPU Load等于CPU个数乘以核数，那么就说CPU正好满负载，再多一点，可能就要出问题了，有些任务不能被及时分配处理器，那要保证响应时间的话，最好要小于CPU个数X核数X0.7。</p>
<p>Load Average是指CPU的Load。它所包含的信息是在一段时间内CPU正在处理及等待CPU处理的进程数之和的统计信息，也就是CPU使用队列的长度的统计信息。<br>Load Average的值应该小于CPU个数X核数X0.7，<br>Load Average会有3个状态平均值，分别是1分钟、5分钟和15分钟平均Load。</p>
<p>如果1分钟平均出现大于CPU个数X核数的情况，还不需要担心；如果5分钟的平均也是这样，那就要警惕了；15分钟的平均也是这样，就要分析哪里出现问题，防范未然。<br>查看CPU负载信息，使用<code>top</code>命令</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">output</span></span><br><span class="line">top - 15:50:31 up 4 days, 23:43, 1 user, load average: 0.51, 0.29, 0.37</span><br><span class="line">Tasks: 492 total,  1 running, 490 sleeping,  1 stopped,  0 zombie</span><br><span class="line">Cpu(s): 6.4%us, 0.1%sy, 0.0%ni, 93.4%id, 0.1%wa, 0.0%hi, 0.0%si, 0.0%st</span><br><span class="line">Mem: 65973912k total, 32468632k used, 33505280k free,  906712k buffers</span><br><span class="line">Swap: 41943032k total,  13204k used, 41929828k free, 6434448k cached</span><br></pre></td></tr></table></figure>


<blockquote>
<p> MacOS查看信息</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">top -l 1 | head -n 10</span><br></pre></td></tr></table></figure>
</blockquote>
<h1 id="5-硬盘"><a href="#5-硬盘" class="headerlink" title="5. 硬盘"></a>5. 硬盘</h1><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">fdisk -l</span><br><span class="line">fdisk -l |grep Disk</span><br></pre></td></tr></table></figure>


<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">output</span></span><br><span class="line">Disk /dev/sda: 500.1 GB, 500107862016 bytes</span><br><span class="line">255 heads, 63 sectors/track, 60801 cylinders, total 976773168 sectors</span><br><span class="line">Units = sectors of 1 * 512 = 512 bytes</span><br><span class="line">Sector size (logical/physical): 512 bytes / 4096 bytes</span><br><span class="line">I/O size (minimum/optimal): 4096 bytes / 4096 bytes</span><br><span class="line">Disk identifier: 0x00023728</span><br><span class="line"></span><br><span class="line">   Device Boot      Start         End      Blocks   Id  System</span><br><span class="line">/dev/sda1   *        2048     2148351     1073152   83  Linux</span><br><span class="line">/dev/sda2         2148352    21680127     9765888   82  Linux swap / Solaris</span><br><span class="line">/dev/sda3        21680128   177930239    78125056   83  Linux</span><br><span class="line">/dev/sda4       177932286   976771071   399419393    5  Extended/dev/sda5       177932288   412305407   117186560   83  Linux</span><br><span class="line">/dev/sda6       412307456   976771071   282231808   83  Linux</span><br></pre></td></tr></table></figure>





<blockquote>
<p> <a target="_blank" rel="noopener" href="https://www.cnblogs.com/ggjucheng/archive/2013/01/14/2859613.html">https://www.cnblogs.com/ggjucheng/archive/2013/01/14/2859613.html</a></p>
</blockquote>
<p> 补：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line">uname -a # 查看内核/操作系统/CPU信息(含x86_64表示32位机器,i686表示32位机器)</span><br><span class="line">head -n 1 /etc/issue # 查看操作系统版本，是数字1不是字母L</span><br><span class="line">cat /proc/cpuinfo # 查看CPU信息的linux系统信息命令</span><br><span class="line">hostname # 查看计算机名的linux系统信息命令</span><br><span class="line">lspci -tv # 列出所有PCI设备</span><br><span class="line">lsusb -tv # 列出所有USB设备的linux系统信息命令</span><br><span class="line">lsmod # 列出加载的内核模块</span><br><span class="line">env # 查看环境变量资源</span><br><span class="line">free -m # 查看内存使用量和交换区使用量</span><br><span class="line">df -h # 查看各分区使用情况</span><br><span class="line">du -sh # 查看指定目录的大小</span><br><span class="line">grep MemTotal /proc/meminfo # 查看内存总量</span><br><span class="line">grep MemFree /proc/meminfo # 查看空闲内存量</span><br><span class="line">uptime # 查看系统运行时间、用户数、负载</span><br><span class="line">cat /proc/loadavg # 查看系统负载磁盘和分区</span><br><span class="line">mount | column -t # 查看挂接的分区状态</span><br><span class="line">fdisk -l # 查看所有分区</span><br><span class="line">swapon -s # 查看所有交换分区</span><br><span class="line">hdparm -i /dev/hda # 查看磁盘参数(仅适用于IDE设备)</span><br><span class="line">dmesg | grep IDE # 查看启动时IDE设备检测状况网络</span><br><span class="line">ifconfig # 查看所有网络接口的属性</span><br><span class="line">iptables -L # 查看防火墙设置</span><br><span class="line">route -n # 查看路由表</span><br><span class="line">netstat -lntp # 查看所有监听端口</span><br><span class="line">netstat -antp # 查看所有已经建立的连接</span><br><span class="line">netstat -s # 查看网络统计信息进程</span><br><span class="line">ps -ef # 查看所有进程</span><br><span class="line">top # 实时显示进程状态用户</span><br><span class="line">w # 查看活动用户</span><br><span class="line">id # 查看指定用户信息</span><br><span class="line">last # 查看用户登录日志</span><br><span class="line">cut -d: -f1 /etc/passwd # 查看系统所有用户</span><br><span class="line">cut -d: -f1 /etc/group # 查看系统所有组</span><br><span class="line">crontab -l # 查看当前用户的计划任务服务</span><br><span class="line">chkconfig –list # 列出所有系统服务</span><br><span class="line">chkconfig –list | grep on # 列出所有启动的系统服务程序</span><br><span class="line">rpm -qa # 查看所有安装的软件包</span><br><span class="line">cat /proc/cpuinfo ：查看CPU相关参数的linux系统命令</span><br><span class="line">cat /proc/partitions ：查看linux硬盘和分区信息的系统信息命令</span><br><span class="line">cat /proc/meminfo ：查看linux系统内存信息的linux系统命令</span><br><span class="line">cat /proc/version ：查看版本，类似uname -r</span><br><span class="line">cat /proc/ioports ：查看设备io端口</span><br><span class="line">cat /proc/interrupts ：查看中断</span><br><span class="line">cat /proc/pci ：查看pci设备的信息</span><br><span class="line">cat /proc/swaps ：查看所有swap分区的信息 </span><br></pre></td></tr></table></figure>


      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2022/05/02/ssh%E5%85%AC%E9%92%A5%E7%A7%81%E9%92%A5%E5%8E%9F%E7%90%86/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2022/05/02/ssh%E5%85%AC%E9%92%A5%E7%A7%81%E9%92%A5%E5%8E%9F%E7%90%86/" class="post-title-link" itemprop="url">ssh公钥私钥原理</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2022-05-02 11:12:14 / 修改时间：15:51:03" itemprop="dateCreated datePublished" datetime="2022-05-02T11:12:14+08:00">2022-05-02</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/" itemprop="url" rel="index"><span itemprop="name">计算机网络</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="ssh公钥私钥原理"><a href="#ssh公钥私钥原理" class="headerlink" title="ssh公钥私钥原理"></a>ssh公钥私钥原理</h1><p>主要有两种登录方式：第一种为密码口令登录，第二种为公钥登录</p>
<h2 id="一、密码登录"><a href="#一、密码登录" class="headerlink" title="一、密码登录"></a>一、密码登录</h2><p>整个过程是这样的：</p>
<ol>
<li><p>远程主机收到用户的登录请求，把自己的公钥发给用户。</p>
</li>
<li><p>用户使用这个公钥，将登录密码加密后，发送到远程主机。（客户端输入密码的过程）</p>
</li>
<li><p>远程主机用自己的私钥，解密登录密码，如果密码正确，就同意用户登录。</p>
</li>
</ol>
<p>这个过程本身是安全的，但是实施的时候存在一个风险：<strong>如果有人截获了登录请求，然后冒充远程主机，将伪造的公钥发给用户，那么用户很难辨别真伪。因为不像https协议，SSH协议的公钥是没有证书中心（CA）公证的，也就是说，都是自己签发的。</strong></p>
<p>如果你是第一次登录对方主机，系统会出现下面的提示：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">　$ </span><span class="language-bash">``ssh` `user@host` `The authenticity of host ``<span class="string">&#x27;host (12.18.429.21)&#x27;</span>` `can<span class="string">&#x27;t be established.` `RSA key fingerprint is 98:2e:d7:e0:de:9f:ac:67:28:c2:42:2d:37:16:58:4d.` `Are you sure you want to ``continue` `connecting (``yes``/no``)?</span></span></span><br></pre></td></tr></table></figure>

<p>这段话的意思是，无法确认host主机的真实性，只知道它的公钥指纹，问你还想继续连接吗？</p>
<p>所谓<strong>公钥指纹</strong>，是指公钥长度较长（这里采用RSA算法，长达1024位），很难比对，所以对其进行MD5计算，将它变成一个128位的指纹。上例中是<code>98:2e:d7:e0:de:9f:ac:67:28:c2:42:2d:37:16:58:4d</code>，再进行比较，就容易多了。</p>
<p>很自然的一个问题就是，用户怎么知道远程主机的公钥指纹应该是多少？回答是没有好办法，<strong>远程主机必须在自己的网站上贴出公钥指纹，以便用户自行核对。假定经过风险衡量以后，用户决定接受这个远程主机的公钥。</strong></p>
<p>当远程主机的公钥被接受以后，它就会被保存在文件<code>$HOME/.ssh/known_hosts</code>之中。下次再连接这台主机，系统就会认出它的公钥已经保存在本地了，从而跳过警告部分，直接提示输入密码。</p>
<p>每个SSH用户都有自己的known_hosts文件，此外系统也有一个这样的文件，通常是&#x2F;etc&#x2F;ssh&#x2F;ssh_known_hosts，保存一些对所有用户都可信赖的远程主机的公钥。</p>
<h2 id="二、公钥登录"><a href="#二、公钥登录" class="headerlink" title="二、公钥登录"></a>二、公钥登录</h2><p><strong>公钥登录是为了解决每次登录服务器都要输入密码的问题，流行使用RSA加密方案</strong>，主要流程包含：</p>
<ol>
<li>客户端生成RSA公钥和私钥（其实在哪生成是无所谓的）</li>
<li>客户端将自己的公钥存放到服务器（如果是服务端生成，则将）</li>
<li>客户端请求连接服务器，服务器将一个随机字符串发送给客户端（发送一个随机串）</li>
<li>客户端根据自己的私钥加密这个随机字符串之后再发送给服务器（客户端私钥加密）</li>
<li>服务器接受到加密后的字符串之后用公钥解密，如果正确就让客户端登录，否则拒绝。这样就不用使用密码了。</li>
</ol>
<p><img src="/ssh%E5%85%AC%E9%92%A5%E7%A7%81%E9%92%A5%E5%8E%9F%E7%90%86/ssh%E5%85%AC%E9%92%A5%E7%A7%81%E9%92%A5%E5%8E%9F%E7%90%861.png" alt="img"></p>
<h3 id="公钥和私钥"><a href="#公钥和私钥" class="headerlink" title="公钥和私钥"></a>公钥和私钥</h3><p>一个公钥对应一个私钥。密钥对中，让大家都知道的是公钥，不告诉大家，只有自己知道的，是私钥。</p>
<ol>
<li>私钥用来进行解密和签名，是给自己用的。</li>
<li>公钥由本人公开，用于加密和验证签名，是给别人用的。</li>
<li>当该用户发送信息时，用私钥签名，别人用他给的公钥验证签名，可以保证该信息是由自己发送的（保证信息的不可篡改）；当该用户接受信息时，别人用他的公钥加密，他用私钥解密，可以保证该信息只能由他接收到（保证信息的私密性）。</li>
</ol>
<h3 id="RSA算法的作用"><a href="#RSA算法的作用" class="headerlink" title="RSA算法的作用"></a>RSA算法的作用</h3><h4 id="1、加密：公钥加密私钥解密"><a href="#1、加密：公钥加密私钥解密" class="headerlink" title="1、加密：公钥加密私钥解密"></a>1、加密：公钥加密私钥解密</h4><p>　　<strong>主要用于将数据资料加密不被其他人非法获取，保证数据私密性。</strong>使用公钥将数据资料加密，只有私钥可以解密。即使密文在网络上被第三方获取由于没有私钥则无法解密。从而保证数据安全性。　　　　　</p>
<ol>
<li>A在自己电脑上生成RSA钥匙文件，一个私钥文件一个公钥文件，并将他的公钥传送给B；</li>
<li>此时B要传送信息给A，于是B用A的公钥加密他的消息，然后传送给A。【网络上传输的密文，没有A的私钥无法解密，其他人获取之后也没用】；</li>
<li>A用他的私钥解密B的消息。</li>
</ol>
<h4 id="2、认证：私钥加密公钥解密"><a href="#2、认证：私钥加密公钥解密" class="headerlink" title="2、认证：私钥加密公钥解密"></a>2、认证：私钥加密公钥解密</h4><p>主要用于身份验证，判断某个身份的真实性。<strong>使用私钥加密之后，用对应的公钥解密从而验证身份真实性。</strong></p>
<p>比如【服务端S】要验证【客户端C】是否是真实用户：</p>
<ol>
<li>C将自己公钥给S；</li>
<li>C将信息用自己私钥加密传送给S；</li>
<li>S根据C的公钥解密，如果成功则为真实身份用户。</li>
</ol>
<p><strong>SSH公钥登录则用的是第二种功能。</strong></p>
<blockquote>
<p>安全性： 这种算法非常可靠，密钥越长，它就越难破解。根据已经披露的文献，目前被破解的最长RSA密钥是768个二进制位。也就是说，长度超过768位的密钥，还无法破解（至少没人公开宣布）。因此可以认为，1024位的RSA密钥基本安全，2048位的密钥极其安全。所以我们在用ssh-keygen命令时候要注意密钥长度，具体参数为：<code>-b bits</code>（指定密钥长度。对于RSA密钥，最小要求768位，默认是2048位。DSA密钥必须恰好是1024位(FIPS 186-2 标准的要求)。）<br>至少不能少于768。一般不用写默认就是2048了。</p>
</blockquote>
<p>注意：ssh 和 https 协议的区别，ssh 没有CA认证中心的概念。</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2022/04/26/ulimit/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2022/04/26/ulimit/" class="post-title-link" itemprop="url">ulimit</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2022-04-26 21:29:48 / 修改时间：21:47:03" itemprop="dateCreated datePublished" datetime="2022-04-26T21:29:48+08:00">2022-04-26</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="ulimit"><a href="#ulimit" class="headerlink" title="ulimit"></a>ulimit</h1><p>  系统性能一直是一个受关注的话题，如何通过最简单的设置来实现最有效的性能调优，如何在有限资源的条件下保证程序的运作，ulimit 是我们在处理这些问题时，经常使用的一种<strong>简单手段</strong>。ulimit 是一种 Linux 系统的内键功能，它具有一套参数集，用于为由<strong>它生成的 shell进程及其子进程</strong>的资源使用设置限制。</p>
<h2 id="ulimit命令"><a href="#ulimit命令" class="headerlink" title="ulimit命令"></a>ulimit命令</h2><p>ulimit 通过一些参数选项来管理不同种类的系统资源。<br>ulimit 命令的格式为：ulimit [options] [limit]</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">选项 含义</span><br><span class="line"> </span><br><span class="line">-a 显示当前系统所有的limit资源信息。 </span><br><span class="line"> </span><br><span class="line">-H 设置硬资源限制，一旦设置不能增加。例如：ulimit – Hs 64；限制硬资源，线程栈大小为 64K。</span><br><span class="line"> </span><br><span class="line">-S 设置软资源限制，设置后可以增加，但是不能超过硬资源设置。例如：ulimit – Sn 32；限制软资源，32 个文件描述符。</span><br><span class="line"> </span><br><span class="line">-c 最大的core文件的大小，以 blocks 为单位。例如：ulimit – c unlimited； 对生成的 core 文件的大小不进行限制。</span><br><span class="line"> </span><br><span class="line">-f 进程可以创建文件的最大值，以blocks 为单位.例如：ulimit – f 2048；限制进程可以创建的最大文件大小为 2048 blocks。</span><br><span class="line"> </span><br><span class="line">-d 进程最大的数据段的大小，以Kbytes 为单位。例如：ulimit -d unlimited；对进程的数据段大小不进行限制。</span><br><span class="line"> </span><br><span class="line">-m 最大内存大小，以Kbytes为单位。例如：ulimit – m unlimited；对最大内存不进行限制。</span><br><span class="line"> </span><br><span class="line">-n 可以打开的最大文件描述符的数量。例如：ulimit – n 128；限制最大可以使用 128 个文件描述符</span><br><span class="line"> </span><br><span class="line">-s 线程栈大小，以Kbytes为单位。例如:ulimit – s 512；限制线程栈的大小为 512 Kbytes。</span><br><span class="line"> </span><br><span class="line">-p 管道缓冲区的大小，以Kbytes 为单位。例如ulimit – p 512；限制管道缓冲区的大小为 512 Kbytes。</span><br><span class="line"> </span><br><span class="line">-u 用户最大可用的进程数。例如 limit – u 65536；限制用户最多可以使用 65536个进程。</span><br><span class="line"> </span><br><span class="line">-v 进程最大可用的虚拟内存，以Kbytes 为单位。ulimit – v 200000；限制最大可用的虚拟内存为 200000 Kbytes。</span><br><span class="line"> </span><br><span class="line">-t 最大CPU占用时间，以秒为单位。ulimit – t unlimited；对最大的 CPU 占用时间不进行限制。</span><br><span class="line"> </span><br><span class="line">-l 最大可加锁内存大小，以Kbytes 为单位。</span><br></pre></td></tr></table></figure>

<h2 id="使用ulimit的几种方案"><a href="#使用ulimit的几种方案" class="headerlink" title="使用ulimit的几种方案"></a>使用ulimit的几种方案</h2><h3 id="在用户的启动脚本中"><a href="#在用户的启动脚本中" class="headerlink" title="在用户的启动脚本中"></a>在用户的启动脚本中</h3><pre><code>    如果用户使用的是 bash，就可以在用户的目录下的 .bashrc 文件中，加入 ulimit – u 64，来限制用户最多可以使用 64 个进程。此外，可以在与 .bashrc 功能相当的启动脚本中加入 ulimt。
</code></pre>
<h3 id="在应用程序的启动脚本中"><a href="#在应用程序的启动脚本中" class="headerlink" title="在应用程序的启动脚本中"></a>在应用程序的启动脚本中</h3><p>如果用户要对某个应用程序 myapp 进行限制，可以写一个简单的脚本 startmyapp。</p>
<p>ulimit – s 512<br>myapp</p>
<p>以后只要通过脚本 startmyapp 来启动应用程序，就可以限制应用程序 myapp 的线程栈大小为 512K。</p>
<h3 id="直接在控制台输入"><a href="#直接在控制台输入" class="headerlink" title="直接在控制台输入"></a>直接在控制台输入</h3><p>ulimit – p 256 </p>
<p>限制管道的缓冲区为 256K。</p>
<h3 id="修改所有-linux-用户的环境变量文件："><a href="#修改所有-linux-用户的环境变量文件：" class="headerlink" title="修改所有 linux 用户的环境变量文件："></a>修改所有 linux 用户的环境变量文件：</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">vi /etc/profile</span><br><span class="line"></span><br><span class="line">ulimit -u 10000</span><br><span class="line"></span><br><span class="line">ulimit -n 4096</span><br><span class="line"></span><br><span class="line">ulimit -d unlimited</span><br><span class="line"></span><br><span class="line">ulimit -m unlimited</span><br><span class="line"></span><br><span class="line">ulimit -s unlimited</span><br><span class="line"></span><br><span class="line">ulimit -t unlimited</span><br><span class="line"></span><br><span class="line">ulimit -v unlimited</span><br></pre></td></tr></table></figure>
<p> 保存后运行#source &#x2F;etc&#x2F;profile 使其生效</p>
<h3 id="也可以针对单个用户的-bash-profile设置："><a href="#也可以针对单个用户的-bash-profile设置：" class="headerlink" title="也可以针对单个用户的.bash_profile设置："></a>也可以针对单个用户的.bash_profile设置：</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">vi ~./.bash_profile</span><br><span class="line"></span><br><span class="line">ulimit -n 1024</span><br></pre></td></tr></table></figure>
<p>重新登陆ok</p>
<h2 id="限制资源的作用范围"><a href="#限制资源的作用范围" class="headerlink" title="限制资源的作用范围"></a>限制资源的作用范围</h2><p>ulimit 作为对资源使用限制的一种工作，是有其作用范围的。那么，它限制的对象是单个用户，单个进程，还是整个系统呢？事实上，ulimit 限制的是当前 shell 进程以及其派生的子进程。举例来说，如果用户同时运行了两个 shell 终端进程，只在其中一个环境中执行了 ulimit – s 100，则该 shell 进程里创建文件的大小收到相应的限制，而同时另一个 shell终端包括其上运行的子程序都不会受其影响。</p>
<h3 id="针对用户永久生效"><a href="#针对用户永久生效" class="headerlink" title="针对用户永久生效"></a>针对用户永久生效</h3><p>那么，是否有针对某个具体用户的资源加以限制的方法呢？答案是有的，方法是通过修改系统的 &#x2F;etc&#x2F;security&#x2F;limits.conf配置文件。该文件不仅能限制指定用户的资源使用，还能限制指定组的资源使用。该文件的每一行都是对限定的一个描述。<br>imits.conf的格式如下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&lt;domain&gt;                  &lt;type&gt;      &lt;item&gt;     &lt;value&gt; </span><br><span class="line">username|@groupname       type        resource          limit</span><br></pre></td></tr></table></figure>
<p>domain：username|@groupname：设置需要被限制的用户名，组名前面加@和用户名区别。也可以用通配符*来做所有用户的限制。</p>
<p>type：有 soft，hard 和 -，soft 指的是当前系统生效的设置值。hard 表明系统中所能设定的最大值。soft 的最大值不能超过hard的值。用 – 就表明同时设置了 soft 和 hard 的值。</p>
<p>resource：<br>    core – 限制内核文件的大小<br>    date – 最大数据大小<br>    fsize – 最大文件大小<br>    memlock – 最大锁定内存地址空间<br>    nofile – 打开文件的最大数目<br>    rss – 最大持久设置大小<br>    stack – 最大栈大小<br>    cpu – 以分钟为单位的最多 CPU 时间<br>    noproc – 进程的最大数目（系统的最大进程数）<br>    as – 地址空间限制<br>    maxlogins – 此用户允许登录的最大数目<br>要使 limits.conf 文件配置生效，必须要确保 pam_limits.so 文件被加入到启动文件中。<br>查看 &#x2F;etc&#x2F;pam.d&#x2F;login 文件中有：session required &#x2F;lib&#x2F;security&#x2F;pam_limits.so</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2022/04/15/Redis-pipeline/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2022/04/15/Redis-pipeline/" class="post-title-link" itemprop="url">Redis的pipeline</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2022-04-15 18:03:53" itemprop="dateCreated datePublished" datetime="2022-04-15T18:03:53+08:00">2022-04-15</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-09-02 22:44:06" itemprop="dateModified" datetime="2022-09-02T22:44:06+08:00">2022-09-02</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Redis/" itemprop="url" rel="index"><span itemprop="name">Redis</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <img src="redis_mget.png" alt="redis_mget" style="zoom:50%;" />



<img src="redis_pipeline.png" alt="redis_pipeline" style="zoom:50%;" />





<ul>
<li><p>普通请求：等待上一条命令应答后再执行，中间不仅仅多了RTT，而且还频繁的调用系统IO，发送网络请求</p>
</li>
<li><p>mget：节省RTT，但是前面的key需要等待最后一个key回复</p>
</li>
<li><p>pineline：节省RTT，减少IO调用次数</p>
</li>
</ul>
<p>RTT(Round-Trip Time): 往返时延。在计算机网络中它是一个重要的性能指标，表示从发送端发送数据开始，到发送端收到来自接收端的确认（接收端收到数据后便立即发送确认），总共经历的时延。</p>
<p>RTT&#x3D;传播时延（往返哒）+排队时延（路由器和交换机的）+数据处理时延（应用程序的）。</p>
<h1 id="Pipeline实现原理"><a href="#Pipeline实现原理" class="headerlink" title="Pipeline实现原理"></a>Pipeline实现原理</h1><p>pipeline管道操作是需要客户端与服务端的支持，客户端将命令写入缓冲，最后再通过exec命令发送给服务端，服务端通过命令拆分，逐个执行返回结果。</p>
<p>​    </p>
<p>pipeline通过减少客户端与redis的通信次数来实现降低往返延时时间，而且Pipeline 实现的原理是队列，而队列的原理是时先进先出，这样就保证数据的顺序性。</p>
<h1 id="Pipeline适用场景"><a href="#Pipeline适用场景" class="headerlink" title="Pipeline适用场景"></a>Pipeline适用场景</h1><p>  Pipeline在某些场景下非常有用，比如有多个command需要被”及时的”提交，而且他们对相应结果没有互相依赖，而且对结果响应也无需立即获得，那么pipeline就可以充当这种”批处理”的工具；而且在一定程度上，可以较大的提升性能,性能提升的原因主要是TCP链接中较少了”交互往返”的时间。</p>
<p> 不过在编码时请注意，pipeline期间将”独占”链接，此期间将不能进行非”管道”类型的其他操作，直到pipeline关闭；比如在上述代码中间，使用jedis.set(key,value)等操作都将抛出异常。</p>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/6844904127001001991">https://juejin.cn/post/6844904127001001991</a></p>
<p><a target="_blank" rel="noopener" href="https://redis.io/docs/manual/pipelining/">https://redis.io/docs/manual/pipelining/</a></p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2022/04/14/curl%E6%A8%A1%E6%9D%BF/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2022/04/14/curl%E6%A8%A1%E6%9D%BF/" class="post-title-link" itemprop="url">curl模板</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2022-04-14 14:16:29" itemprop="dateCreated datePublished" datetime="2022-04-14T14:16:29+08:00">2022-04-14</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-05-28 11:40:33" itemprop="dateModified" datetime="2022-05-28T11:40:33+08:00">2022-05-28</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">curl -H &quot;Content-type: application/json&quot; -X POST </span><br><span class="line">-d &#x27;$json&#x27; &#x27;$url&#x27;</span><br></pre></td></tr></table></figure>

<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">curl文件下载</span><br><span class="line">curl将下载文件输出到stdout，将进度信息输出到stderr，不显示进度信息使用–silent 选项。</span><br><span class="line"></span><br><span class="line">1. curl URL --silent</span><br><span class="line">这条命令是将下载文件输出到终端，所有下载的数据都被写入到stdout。</span><br><span class="line"></span><br><span class="line">2. curl URL --silent -O</span><br><span class="line">使用选项 -O 将下载的数据写入到文件，必须使用文件的绝对地址。</span><br><span class="line"></span><br><span class="line">3. curl URL -o filename --progress</span><br><span class="line"><span class="meta prompt_">#</span><span class="language-bash"><span class="comment">######################################## 100.0%</span></span></span><br><span class="line">选项 -o 将下载数据写入到指定名称的文件中，并使用 –progress 显示进度条。</span><br></pre></td></tr></table></figure>



<p>比如:<code>curl http://211.87.224.233:9999/file/sftp/sdu-weblab.helloworld.jingtao.tar.gz -o sdu-weblab.helloworld.jingtao.tar.gz  </code></p>
<p>${var##*&#x2F;}</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2022/04/03/StartDocker/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2022/04/03/StartDocker/" class="post-title-link" itemprop="url">Docker入门</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2022-04-03 18:16:01" itemprop="dateCreated datePublished" datetime="2022-04-03T18:16:01+08:00">2022-04-03</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-10-18 18:58:37" itemprop="dateModified" datetime="2022-10-18T18:58:37+08:00">2022-10-18</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Docker/" itemprop="url" rel="index"><span itemprop="name">Docker</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <p>系列文章</p>
<p><a target="_blank" rel="noopener" href="https://learn.lianglianglee.com/%E4%B8%93%E6%A0%8F/%E7%94%B1%E6%B5%85%E5%85%A5%E6%B7%B1%E5%90%83%E9%80%8F%20Docker-%E5%AE%8C">由浅入深吃透Docker</a></p>
<p>Docker 是<strong>一个用于开发、发布和运行应用程序的开放平台</strong>。Docker 使您能够<strong>将应用程序与基础架构分离</strong>，以便您可以快速交付软件。使用 Docker，您可以像管理应用程序一样管理基础设施。通过利用 Docker 快速交付、测试和部署代码的方法，您可以显着减少编写代码和在生产环境中运行之间的延迟。</p>
<h2 id="我可以使用-Docker-做什么？"><a href="#我可以使用-Docker-做什么？" class="headerlink" title="我可以使用 Docker 做什么？"></a>我可以使用 Docker 做什么？</h2><ul>
<li><strong>快速、一致地交付您的应用程序</strong><br>Docker 通过允许开发人员使用提供应用程序和服务的本地容器在标准化环境中工作来简化开发生命周期。</li>
</ul>
<p>也就是说，<strong>容器的概念非常适合持续集成和持续交付 (CI&#x2F;CD) 工作流程。</strong></p>
<ul>
<li><strong>响应式部署和扩展</strong></li>
</ul>
<p>Docker 基于容器的平台允许高度可移植的工作负载。Docker 容器可以在开发人员的本地笔记本电脑、数据中心的物理或虚拟机、云提供商或混合环境中运行。</p>
<p>Docker 的可移植性和轻量级特性还使得动态管理工作负载、根据业务需求<strong>近乎实时地扩展或拆除应用程序和服务变得容易</strong>。</p>
<ul>
<li><strong>在相同硬件上运行更多工作负载</strong><blockquote>
<p>Docker 是轻量级和快速的。它为基于管理程序的虚拟机提供了一种可行且经济高效的替代方案，因此您可以使用更多的计算容量来实现业务目标。Docker 非常适合高密度环境下需要用更少资源完成更多工作的中小型部署。</p>
</blockquote>
</li>
</ul>
<p>也就是说，相比对docker更加可靠。</p>
<h2 id="Docker-architecture"><a href="#Docker-architecture" class="headerlink" title="Docker architecture"></a>Docker architecture</h2><p>Docker 使用客户端-服务器架构。Docker客户端与 Docker守护进程对话，后者负责构建、运行和分发 Docker 容器的繁重工作。Docker 客户端和守护程序可以在同一系统上运行，或者您可以将 Docker 客户端连接到远程 Docker 守护程序。Docker 客户端和守护程序使用 REST API，通过 UNIX 套接字或网络接口进行通信。另一个 Docker 客户端是 Docker Compose，它允许您使用由一组容器组成的应用程序。</p>
<p><img src="/2022/04/03/StartDocker/architecture.svg" alt="Docker Architecture Diagram"></p>
<h3 id="The-Docker-daemon（Docker-守护进程）"><a href="#The-Docker-daemon（Docker-守护进程）" class="headerlink" title="The Docker daemon（Docker 守护进程）"></a>The Docker daemon（Docker 守护进程）</h3><p>Docker 守护程序 ( <code>dockerd</code>) 侦听 Docker API 请求并管理 Docker 对象，例如 images, containers, networks, and volumes（镜像、容器、网络和数据卷）。守护进程还可以与其他守护进程通信以管理 Docker 服务。</p>
<h3 id="The-Docker-client"><a href="#The-Docker-client" class="headerlink" title="The Docker client"></a>The Docker client</h3><p>Docker 客户端 ( <code>docker</code>) 是许多 Docker 用户与 Docker 交互的主要方式。当您使用诸如<code>docker run</code>之类的命令时，客户端会将这些命令发送到<code>dockerd</code>执行它们。该<code>docker</code>命令使用 Docker API。Docker 客户端可以与多个守护进程通信。</p>
<h3 id="Docker-Desktop"><a href="#Docker-Desktop" class="headerlink" title="Docker Desktop"></a>Docker Desktop</h3><p>Docker Desktop 是一个易于安装的应用程序，适用于您的 Mac 或 Windows 环境，使您能够构建和共享容器化应用程序和微服务。Docker Desktop 包括 Docker 守护程序 ( <code>dockerd</code>)、Docker 客户端 ( <code>docker</code>)、Docker Compose、Docker Content Trust、Kubernetes 和 Credential Helper。</p>
<h3 id="Docker-registries（注册中心）"><a href="#Docker-registries（注册中心）" class="headerlink" title="Docker registries（注册中心）"></a>Docker registries（注册中心）</h3><p>Docker<em>注册中心</em>存储 Docker 镜像。</p>
<p>Docker Hub 是一个任何人都可以使用的公共注册中心，并且 Docker 默认配置为在 Docker Hub 上查找镜像。您甚至可以运行自己的私有注册中心。</p>
<p>当您使用<code>docker pull</code> or <code>docker run</code>命令时，将从您配置的注册中心中提取所需的镜像。</p>
<p>当您使用该<code>docker push</code>命令时，您的镜像会被推送到您配置的注册中心中。</p>
<h3 id="Docker-objects"><a href="#Docker-objects" class="headerlink" title="Docker objects"></a>Docker objects</h3><h4 id="Images（镜像）"><a href="#Images（镜像）" class="headerlink" title="Images（镜像）"></a>Images（镜像）</h4><p><em>镜像</em>是一个只读模板，其中包含创建 Docker 容器的指令。</p>
<p>通常，一个镜像基于另一个镜像，并带有一些额外的自定义。例如，您可以基于<code>ubuntu</code>映像构建一个自己的映像 ，而这个自定义的镜像安装了 Apache Web Server和自己的应用程序，以及使得应用程序运行所需的配置信息。</p>
<p>你可以创建自己的镜像，也可以只使用其他人创建并在注册中心中发布的镜像。 </p>
<p>要构建您自己的镜像，你需要使用简单的语法创建一个<em>Dockerfile</em> ，这个docker file便是用于定义创建和运行镜像所需的步骤。</p>
<p>Dockerfile 中的每条指令都会在映像中创建一个层（layer）。当您更改 Dockerfile 并重建映像时，仅重建那些已更改的层。与其他虚拟化技术相比，这是使映像如此轻量、小巧和快速的部分原因。</p>
<h4 id="Containers"><a href="#Containers" class="headerlink" title="Containers"></a>Containers</h4><p><em>容器</em>是镜像的可运行实例。</p>
<p>您可以<strong>使用 Docker API 或 CLI 创建、启动、停止、移动或删除（create, start, stop, move, or delete）容器</strong>。您可以将容器连接到一个或多个网络，将存储附加到它，甚至可以根据其当前状态创建新映像。</p>
<p>默认情况下，一个容器与其他容器及其主机的隔离相对较好。您可以控制容器的网络、存储或其他底层子系统与其他容器或主机的隔离程度。</p>
<p><strong>容器由其映像以及您在创建或启动它时提供给它的配置选项定义</strong>。</p>
<p>当容器被移除时，任何未存储在持久存储介质中的状态更改都会消失。</p>
<h5 id="示例docker-run命令"><a href="#示例docker-run命令" class="headerlink" title="示例docker run命令"></a>示例<code>docker run</code>命令</h5><p>以下命令运行一个<code>ubuntu</code>容器，以交互方式附加到您的本地命令行会话，然后运行<code>/bin/bash</code>.</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker run -i -t ubuntu /bin/bash</span><br></pre></td></tr></table></figure>

<p>当您运行此命令时，会发生以下情况（假设您使用的是默认注册中心配置）：</p>
<ol>
<li>如果您在本地没有<code>ubuntu</code>映像，Docker 会从您配置的注册中心中提取它，就像您<code>docker pull ubuntu</code>手动运行一样。</li>
<li>Docker 会创建一个新容器，就像您<code>docker container create</code> 手动运行命令一样。</li>
<li>Docker 为容器分配一个读写文件系统，作为它的最后一层。这允许正在运行的容器在其本地文件系统中创建或修改文件和目录。</li>
<li>Docker 创建了一个网络接口来将容器连接到默认网络，因为您没有指定任何网络选项（这是一个默认行为）。这包括为容器分配 IP 地址。默认情况下，容器可以使用主机的网络连接连接到外部网络。</li>
<li>Docker 启动容器并执行<code>/bin/bash</code>. 因为容器以交互方式运行并附加到您的终端（由于<code>-i</code>and<code>-t</code> 标志），您可以在输出记录到终端时使用键盘提供输入。</li>
<li>当您键入<code>exit</code>终止<code>/bin/bash</code>命令时，容器会停止但不会被删除。您可以重新启动或删除它。</li>
</ol>
<h2 id="底层技术"><a href="#底层技术" class="headerlink" title="底层技术"></a>底层技术</h2><p>Docker 是用Go 编写的，并利用 Linux 内核的几个特性来提供其功能。Docker 使用一种称为<code>namespaces</code>的技术来提供隔离的工作空间（也就是所说的<em>容器</em>的概念）。当您运行容器时，Docker 会为该容器创建一组 <em>命名空间。</em></p>
<p>这些命名空间提供了一层隔离。容器的每个方面都在单独的命名空间中运行，并且它的访问权限仅限于该命名空间。</p>
<blockquote>
<p>参考</p>
<p><a target="_blank" rel="noopener" href="https://docs.docker.com/get-started/overview/">https://docs.docker.com/get-started/overview/</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2022/03/29/%E6%9C%8D%E5%8A%A1%E6%B3%A8%E5%86%8C%E4%B8%8E%E5%8F%91%E7%8E%B0/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2022/03/29/%E6%9C%8D%E5%8A%A1%E6%B3%A8%E5%86%8C%E4%B8%8E%E5%8F%91%E7%8E%B0/" class="post-title-link" itemprop="url">服务注册与发现</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2022-03-29 03:51:11 / 修改时间：03:54:51" itemprop="dateCreated datePublished" datetime="2022-03-29T03:51:11+08:00">2022-03-29</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%88%86%E5%B8%83%E5%BC%8F/" itemprop="url" rel="index"><span itemprop="name">分布式</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="服务注册与发现基本原理"><a href="#服务注册与发现基本原理" class="headerlink" title="服务注册与发现基本原理"></a>服务注册与发现基本原理</h1><p>服务注册与发现是分为注册和发现两个关键的步骤。</p>
<p><strong>服务注册</strong>：服务进程在注册中心注册自己的元数据信息。通常包括主机和端口号，有时还有身份验证信息，协议，版本号，以及运行环境的信息。</p>
<p><strong>服务发现</strong>：客户端服务进程向注册中心发起查询，来获取服务的信息。服务发现的一个重要作用就是提供给客户端一个可用的服务列表。</p>
<h2 id="服务注册"><a href="#服务注册" class="headerlink" title="服务注册"></a>服务注册</h2><p>注册是服务自己要负责注册与注销的工作。当服务启动后注册线程向注册中心注册，当服务下线时注销自己</p>
<h2 id="服务发现"><a href="#服务发现" class="headerlink" title="服务发现"></a>服务发现</h2><p>服务发现分为客户端发现和代理发现。</p>
<p><strong>客户端发现</strong></p>
<p>客户端发现是指客户端负责向注册中心查询可用服务地址，获取到所有的可用实例地址列表后客户端根据负载均衡算法选择一个实例发起请求调用。</p>
<p>这种方式非常直接，客户端可以控制负载均衡算法。</p>
<p>但是缺点也很明显，获取实例地址、负载均衡等逻辑与服务的业务逻辑耦合在一起，如果服务发现或者负载平衡有变化，那么所有的服务都要修改重新上线。</p>
<p><strong>代理发现</strong></p>
<p>代理发现是指新增一个路由服务负责服务发现获取可用的实例列表，服务消费者如果需要调用服务A的一个实例可以直接将请求发往路由服务，路由服务根据配置好的负载均衡算法从可用的实例列表中选择一个实例将请求转发过去即可。</p>
<p>如果发现实例不可用，路由服务还可以自行重试，服务消费者完全不用感知。</p>
<h2 id="心跳机制"><a href="#心跳机制" class="headerlink" title="心跳机制"></a>心跳机制</h2><p>如果服务有多个实例，其中一个实例出现宕机，注册中心是可以实时感知到，并且将该实例信息从列表中移出，也称为摘机。</p>
<p>如何实现摘机？业界比较常用的方式是通过心跳检测的方式实现，心跳检测有<strong>主动</strong>和<strong>被动</strong>两种方式。</p>
<p><strong>被动检测</strong>是指服务主动向注册中心发送心跳消息，时间间隔可自定义，比如配置5秒发送一次，注册中心如果在三个周期内比如说15秒内没有收到实例的心跳消息，就会将该实例从列表中移除。</p>
<p><strong>主动检测</strong>是注册中心主动发起，每隔几秒中会给所有列表中的服务实例发送心跳检测消息，如果多个周期内未发送成功或未收到回复就会主动移除该实例。</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2022/03/27/%E8%A3%85%E9%A5%B0%E8%80%85%E6%A8%A1%E5%BC%8F%E5%92%8CJava%E4%B8%ADIO%E6%B5%81/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2022/03/27/%E8%A3%85%E9%A5%B0%E8%80%85%E6%A8%A1%E5%BC%8F%E5%92%8CJava%E4%B8%ADIO%E6%B5%81/" class="post-title-link" itemprop="url">装饰者模式和Java中IO流</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2022-03-27 21:29:37" itemprop="dateCreated datePublished" datetime="2022-03-27T21:29:37+08:00">2022-03-27</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-09-15 14:12:18" itemprop="dateModified" datetime="2022-09-15T14:12:18+08:00">2022-09-15</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/" itemprop="url" rel="index"><span itemprop="name">设计模式</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h2 id="1-1-什么是装饰者模式"><a href="#1-1-什么是装饰者模式" class="headerlink" title="1.1 什么是装饰者模式"></a>1.1 什么是装饰者模式</h2><p><strong>装饰者模式指的是在不必改变原类文件和使用继承的情况下，动态地扩展一个对象的功能</strong>。</p>
<p>它是通过创建一个包装对象，也就是装饰者来包裹真实的对象。<br>所以装饰者可以动态地将责任附加到对象上。若要扩展功能，装饰者提供了比继承更有弹性的方案。</p>
<blockquote>
<p>装饰者模式，就是在原有的基类的基础上，给对象添加一些额外的职能，对对象进行包装和修饰，以此来展示出一个更为复杂的、功能更为齐全的新对象。装饰者模式跟子类继承的方式非常类似，都是在基类的基础上拓展功能，但是比基类更加的灵活，也符合多组合、少继承的设计原则，避免对象之间的耦合。</p>
</blockquote>
<h2 id="1-2-装饰者模式组成结构"><a href="#1-2-装饰者模式组成结构" class="headerlink" title="1.2 装饰者模式组成结构"></a>1.2 装饰者模式组成结构</h2><ul>
<li>抽象构件 (Component)：给出抽象接口或抽象类，以规范准备接收附加功能的对象。</li>
<li>具体构件 (ConcreteComponent)：定义将要接收附加功能的类。</li>
<li>抽象装饰 (Decorator)：装饰者共同要实现的接口，也可以是抽象类。</li>
<li>具体装饰 (ConcreteDecorator)：持有一个 Component 对象，负责给构件对象“贴上”附加的功能。</li>
</ul>
<h2 id="1-3-装饰者模式-UML-图解"><a href="#1-3-装饰者模式-UML-图解" class="headerlink" title="1.3 装饰者模式 UML 图解"></a>1.3 装饰者模式 UML 图解</h2><p><img src="/2022/03/27/%E8%A3%85%E9%A5%B0%E8%80%85%E6%A8%A1%E5%BC%8F%E5%92%8CJava%E4%B8%ADIO%E6%B5%81/%E8%A3%85%E9%A5%B0%E8%80%85%E6%A8%A1%E5%BC%8FUML.png"></p>
<h2 id="1-4-装饰者模式应用场景"><a href="#1-4-装饰者模式应用场景" class="headerlink" title="1.4 装饰者模式应用场景"></a>1.4 装饰者模式应用场景</h2><ul>
<li>需要扩展一个类的功能，或给一个类添加附加职责。</li>
<li>需要动态的给一个对象添加功能，这些功能可以再动态的撤销。</li>
<li><strong>需要增加由一些基本功能的排列组合而产生的非常大量的功能，从而使继承关系变的不现实。</strong></li>
<li><strong>当不能采用生成子类的方法进行扩充时。可能有大量独立的扩展，为支持每一种组合将产生大量的子类，使得子类数目呈爆炸性增长。</strong></li>
</ul>
<h2 id="1-5-装饰者模式特点"><a href="#1-5-装饰者模式特点" class="headerlink" title="1.5 装饰者模式特点"></a>1.5 装饰者模式特点</h2><ul>
<li><strong>装饰者对象和具体构件有相同的接口</strong>。这样客户端对象就能以和真实对象相同的方式和装饰对象交互。</li>
<li><strong>可以使用一个或多个装饰对象包装一个对象</strong>。</li>
<li>装饰者对象包含一个具体构件的引用（reference）。</li>
<li>装饰者对象接受所有来自客户端的请求。它把这些请求转发给具体构件。</li>
<li>装饰者对象可以在转发这些请求以前或以后动态增加一些功能。</li>
</ul>
<h2 id="1-6-装饰者模式的优缺点"><a href="#1-6-装饰者模式的优缺点" class="headerlink" title="1.6 装饰者模式的优缺点"></a>1.6 装饰者模式的优缺点</h2><p>装饰者模式与继承关系的目的都是要扩展对象的功能，但是 Decorator 可以提供比继承更多的灵活性。<br>通过使用不同的具体装饰类以及这些装饰类的排列组合，设计者可以创造出很多不同行为的组合。<br>缺点</p>
<p>这种比继承更加灵活机动的特性，也同时意味着更加多的复杂性。<br>装饰模式会导致设计中出现许多小类 (I&#x2F;O 类中就是这样)，如果过度使用，会使程序变得很复杂。<br>装饰模式是针对抽象组件（Component）类型编程。但是，如果你要针对具体组件编程时，就应该重新思考你的应用架构，以及装饰者是否合适。</p>
<h2 id="装饰器和代理模式的区别"><a href="#装饰器和代理模式的区别" class="headerlink" title="装饰器和代理模式的区别"></a>装饰器和代理模式的区别</h2><blockquote>
<p>对装饰器模式来说，装饰者（Decorator）和被装饰者（Decoratee）都实现一个接口。对代理模式来说，代理类（Proxy Class）和真实处理的类（Real Class）都实现同一个接口。此外，不论我们使用哪一个模式，都可以很容易地在真实对象的方法前面或者后面加上自定义的方法。</p>
<p>在上面的例子中，装饰器模式是使用的调用者从外部传入的被装饰对象（coffee），调用者只想要你把他给你的对象装饰（加强）一下。而代理模式使用的是代理对象在自己的构造方法里面new的一个被代理的对象，不是调用者传入的。调用者不知道你找了其他人，他也不关心这些事，只要你把事情做对了即可。</p>
<p>装饰器模式关注于在一个对象上动态地添加方法，而代理模式关注于控制对对象的访问。换句话说，<strong>用代理模式，代理类可以对它的客户隐藏一个对象的具体信息。因此当使用代理模式的时候，我们常常在一个代理类中创建一个对象的实例；当使用装饰器模式的时候，我们通常的做法是将原始对象作为一个参数传给装饰器的构造器。</strong></p>
<p>装饰器模式和代理模式的使用场景不一样，</p>
<p>比如IO流使用的是<strong>装饰者模式，可以层层增加功能</strong>；而代理模式则<strong>一般是用于增加特殊的功能，有些动态代理不支持多层嵌套</strong>。</p>
<p>代理和装饰其实从另一个角度更容易去理解两个模式的区别：代理更多的是强调对对象的访问控制，比如说，访问A对象的查询功能时，访问B对象的更新功能时，访问C对象的删除功能时，都需要判断对象是否登陆，那么我需要将判断用户是否登陆的功能抽提出来，并对A对象、B对象和C对象进行代理，使访问它们时都需要去判断用户是否登陆，简单地说就是将某个控制访问权限应用到多个对象上；而装饰器更多的强调给对象加强功能，比如说要给只会唱歌的A对象添加跳舞功能，添加说唱功能等，简单地说就是将多个功能附加在一个对象上。</p>
<p>所以，<strong>代理模式注重的是对对象的某一功能的流程把控和辅助</strong>，它可以控制对象做某些事，重心是为了借用对象的功能完成某一流程，而非对象功能如何。而<strong>装饰模式注重的是对对象功能的扩展，不关心外界如何调用，只注重对对象功能加强，装饰后还是对象本身</strong>。</p>
</blockquote>
<blockquote>
<p><strong>总结</strong></p>
<p>对于代理类，如何调用对象的某一功能是思考重点，而不需要兼顾对象的所有功能；对于装饰类，如何扩展对象的某一功能是思考重点，同时也需要兼顾对象的其他功能，因为再怎么装饰，本质也是对象本身，要担负起对象应有的职责，被装饰者的职责一旦增加，作为装饰类也需要有相应的扩展，必然会造成编码的负担。</p>
<p>设计模式本身是为了提升代码的可扩展性，灵活应用即可，不必生搬硬套，非要分出个所以然来，装饰器模式和代理模式的区别也是如此。</p>
</blockquote>
<blockquote>
<p>参考</p>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/6844904205723910151">https://juejin.cn/post/6844904205723910151</a></p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/hustzw07/article/details/80795855">https://blog.csdn.net/hustzw07/article/details/80795855</a></p>
<p><a target="_blank" rel="noopener" href="https://segmentfault.com/a/1190000016508992">https://segmentfault.com/a/1190000016508992</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




  <nav class="pagination">
    <a class="extend prev" rel="prev" title="上一页" aria-label="上一页" href="/page/10/"><i class="fa fa-angle-left"></i></a><a class="page-number" href="/">1</a><span class="space">&hellip;</span><a class="page-number" href="/page/10/">10</a><span class="page-number current">11</span><a class="page-number" href="/page/12/">12</a><span class="space">&hellip;</span><a class="page-number" href="/page/26/">26</a><a class="extend next" rel="next" title="下一页" aria-label="下一页" href="/page/12/"><i class="fa fa-angle-right"></i></a>
  </nav>

</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2023</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">SongyangJi</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/muse/" rel="noopener" target="_blank">NexT.Muse</a> 强力驱动
  </div>

    </div>
  </footer>

  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>
  <div class="sidebar-dimmer"></div>
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up fa-lg"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/schemes/muse.js"></script><script src="/js/next-boot.js"></script>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/hexo-generator-searchdb/1.4.1/search.js" integrity="sha256-1kfA5uHPf65M5cphT2dvymhkuyHPQp5A53EGZOnOLmc=" crossorigin="anonymous"></script>
<script src="/js/third-party/search/local-search.js"></script>





  





</body>
</html>
